equilibrium engine · one engine · two worlds

Welcome to the journey.
Which world do you want to explore first?

same engine underneath — switch anytime

EQUILIBRIUMEngine
repo ↗

01 the investigation begins · ⌁ in Night City

The same words.
Two different reactions.
No randomness.

Equilibrium Engine models RPG characters as inspectable inner dynamics: events disturb an internal balance, memory carries tension forward, and visible behaviour emerges when a threshold is crossed.

Deterministic. Debuggable. The same inputs always produce the same life.

Pencil-sketch profile head wrapped in a feedback loop: a blue sine wave enters from the left, an orange step function exits right, a comparator block feeds back beneath, with a balance scale under the EQUILIBRIUM ENGINE wordmark. Pencil-sketch profile head wrapped in a feedback loop: a blue sine wave enters from the left, an orange step function exits right, a comparator block feeds back beneath, with a balance scale under the EQUILIBRIUM ENGINE wordmark.

02 first discovery

The split happens before the trigger.

The visible outburst happens at 09:40 — but the cause started hours earlier. Rain on the neon strip, frustration, fatigue, and self-control were already shifting the internal equilibrium. The remark only crossed the threshold.

Investigation board: one identical remark at 09:40 branches into a held outcome on the calm blue timeline and an erupted outcome on the orange one — the difference is upstream. Investigation board: one identical remark at 09:40 branches into a held outcome on the calm blue timeline and an erupted outcome on the orange one — the difference is upstream.
fig.02 One remark. Two prior trajectories. The divergence is earlier than the words.
Fair morning → he holds it.
a flat word, a held breath
Cold, rainy morning → he erupts.
the same remark lands on a system near its limit
The remark from Marta is identical in both. The difference is upstream.

But what changed before the remark? Look inside ↓


03 the reveal

What the story did not show.

Outside, it looked like a sudden temper. Inside, it was a deterministic state trajectory. The same words arrive at the same minute on both mornings — but on the cold run, frustration and stress have been accumulating since dawn, self-control has eroded, and the trigger lands on a system already near its limit. Not random — inspectable dynamics.

Two stacked morning state-traces, a fair run and a rainy run, sharing one remark; on the rainy run the line crosses a dashed threshold and triggers an outburst. Two stacked morning state-traces, a fair run and a rainy run, sharing one remark; on the rainy run the line crosses a dashed threshold and triggers an outburst.
fig.03 Same event marker. Different prior trajectories. One crosses the threshold; one doesn't.

on the divergence

Same event marker. Different prior trajectories. One crosses the threshold; one doesn't.

on the slow line

And one state — resentment toward Marta — moves on a slower clock than the rest. Hold that thought.

How does an event become these state changes? Open the loop ↓


04 the mechanism

The inner loop.

The engine does not script emotions. Each tick, it turns an event into semantic inputs, filters them through what the character feels about who and what the event involves, updates a small set of state variables (which accumulate and decay), lets sparse feedback couplings pull them on each other, computes a reaction potential, and emits a trace explaining exactly why it did what it did.

Left-to-right signal-flow pipeline of seven stages with a feedback loop curving back and a comparator and threshold near the output. Left-to-right signal-flow pipeline of seven stages with a feedback loop curving back and a comparator and threshold near the output.
fig.04 event → semantic input → memory filters → internal states → feedback loops → reaction potential → visible behavior.
Show the update rule advanced

Every state is the same generic element — a value that retains part of its past, drifts toward a resting level, and sums what arrives this tick from the world and from the other states:

new = decay·old + (1 − decay)·setpoint + drift
        + Σ gain·input          (events, after the relational / affinity filters)
        + Σ coupling·other_state (sparse feedback between states)

No numeric literals live in the engine — the topology is fixed; the constants come from an offline calibration pass. Behaviour is selected from the resulting state, never from an if insulted → angry rule.

States change — but what lasts? Stay overnight ↓


05 the slow memory

Sleep clears the mood. It does not erase the wound.

Fast states — anger, stress, frustration — discharge overnight; the character wakes calm. But the relationship states leak far more slowly, and sleep never touches them. Branic wakes rested and even-tempered — and a little colder toward Marta than he was yesterday. The temper was today. The grudge is a relationship.

Slow-memory graph: a fast anger line spikes and returns to calm across a shaded night window while a slow resentment line builds and stays elevated into the next day. Slow-memory graph: a fast anger line spikes and returns to calm across a shaded night window while a slow resentment line builds and stays elevated into the next day.
fig.05 anger resets overnight · resentment → Marta persists, and hardens if it happens again.

The same dynamics, pushed further — choose a scenario ↓


06 step inside

Pick a scenario. Open its world.

The same small set of dynamics, pushed four ways — across two worlds, scaled up to a whole city, and read whole, tick by tick. Open any block to step inside.

And the boldest claim of all: none of this depends on the world. See the proof ↓


07 the proof

The body stays. The costume changes.

The language model is allowed to change the skin — a frontier keep, a neon bar, a court intrigue, a colony ship — but the deterministic run underneath stays the same. Anger, the displaced flare, the grudge that survives the night: identical numbers, different words. This page does it too — you chose a costume at the gate, not a content.

You are reading Night City. Every number on this page stays put when you

fig.07 Same run underneath — a frontier keep and a neon bar, tied by one balance.
character ⚔ Keep · now showing ⌁ Night City in a line
Eddacastellanfloor boss / fixercomposed, respected; command is her default mode
Halgrimsergeant of the watchhead of the dooriron self-control; never raises his voice
Wojsławman-at-armsmerc regularproud, thin-skinned; an insult is a spark
Cichyprisoner in the undercrofta debtor in the back roomhates his captor; a “kindness” deepens it
Branicgreen recruitrookie bouncereager; feelings close to the surface; bores fast
Lutekbard at the keeprockerboy on the cornerall wit; an insult slides off him
Welfkeep handbarbackidle, fidgety; takes almost nothing to heart
Martathe cookthe cookthe cabbage soup is hers — and it stays soup in both worlds

One more night — all seven of them, side by side. Stay up ↓


08 the night closes in

Every night empties the day. It does not empty the grudge.

Run the same three days through all seven. Sleep discharges the day's heat for every one of them — anger climbs through the wet, tedious door shift and falls to nothing by each dawn. But how each comes to stand toward Marta can outlast the night: where the remark booked a grudge, the grudge is still there in the morning; where someone warmed to her soup, the warmth stays too. The same night. Seven different mornings.

the remark night night night mood · anger (resets) standing → Marta (persists · grudge ↑ / warmth ↓) Wojsław a noble guest wakes a touch cooler Halgrim the watch sergeant warms to her soup Cichy a prisoner the grudge hardens Branic a green recruit wakes a touch cooler Edda the castellan warms to her soup Lutek a wandering bard warms to her soup Welf a stranded merchant warms to her soup Day 1 Day 2 Day 3
fig.08 Same reset, different residue — plotted from one deterministic engine run. The mood returns to calm for all seven; the standing toward Marta does not: a grudge hardens here, a soup-warmth deepens there.
embedded reader · one_day_all_personas.html read all seven across three nights ↗

appendix · for the curious

Field notes from the engine.

Built A temper that builds and breaks

Needling someone already strained snaps them; the same words get a shrug when they're fresh.

Built Same scene, different play

A blind believability panel rated seven characters built only from parameter sheets as in-character, without ever seeing the parameters.

Built Physiology shifts the mood

Hunger, fatigue, and a cold-rain channel erode self-control; displaced strain from an unrelated cause, not a special case.

Built Authority that ripples

One character's order becomes another's event next tick, resolved through the same obedience pipeline: obey the respected officer, resent the despised one — with the same order.

Built Believability you can regression-test

Characters are parameter sheets, runs are deterministic; “did my tweak make him out of character?” becomes a test you run in CI.

Aspirational Societies, rumour, grief, addiction, campaign-long bonds

Each names the real mechanism it would extend — slow states with floors, the cross-agent router, emergent loops. Clearly marked as where it could go.


the last scene

Look back the way you came.

You walked through a keep.
You walked through a night city.

The costumes changed.
The signals did not.

That is the point.

Equilibrium Engine does not script a world.
It models the dynamics beneath it.

Step inside the model → Build your own scenario ↗


the gate stands open

This keep is open source. Walk in.

Everything underneath this page — the integrators, the filters, the grudges — is Apache-2.0 and public. Every plot you scrolled past was drawn from a run you can reproduce bit-for-bit on your own machine. And the keep is short of everything that matters: personas, scenarios, whole worlds to wear. The engine is small on purpose. The cast doesn't have to be.

01
Break a character

Open the live playground and push a persona until it stops being itself. If you manage it, tell us how — “he went out of character” is a bug report here, and we treasure those.

02
Write a character

A persona is a parameter sheet, not code — half-lives, gains, a few relations. Bring a jealous innkeeper, a homesick golem, a saint with one weakness. If it has tensions, the dynamics can carry it.

03
Bend the engine

Pure functions with explicit contracts, a sparse frozen topology, property tests instead of magic numbers. If you’ve ever stabilised a control loop — or wanted to learn on one that holds grudges — start at the issues.

reserved · artwork assets/open_gate-dark.webp · assets/open_gate-light.webp the keep’s gate, standing open — travellers’ tools left against the wall The keep’s gate standing open at dusk, a lantern lit for whoever arrives next. Pencil sketch of the keep’s gate standing open, a lantern lit for whoever arrives next.

No CLA theatre. Apache-2.0, deterministic tests, and a maintainer who reads every issue.

who keeps this keep

Robert Malczyk

Robert builds characters like control systems.

First the wiring.
Then the calibration.
Then the uncomfortable question: can the same conditions produce the same wound twice?

Equilibrium Engine is his bet that believable characters are not written by adding more scripts. They emerge from dynamics you can inspect, disturb, stabilise, and break.

This page is the experiment running in public — one trigger, one trace, one grudge at a time.

He reads every issue, and he is genuinely delighted when someone breaks a persona.