Skip to content
Devlog

Making a collapse repeat exactly

An office building collapsing in a cloud of dust in an early test scene, September 29

This covers September 29 to October 1, 2026. The engine’s results are deterministic on one machine: the same build, seed and inputs reach the same state. This is how we got there and how we keep it.

Two recordings that disagreed

On September 29, two recordings of the same test scene gave different results. The scene is an office with charges set off at its front columns. In one recording 696 members, the structural pieces of the building, fell. In the other, 1,123 did.

We followed the rules the physics engine’s own documentation lists for repeatable results:

  • Queries that find bodies in a box recheck each hit against the body’s real bounds and act in body ID order.
  • The list of active bodies, impact events and contacts all come back sorted.
  • Recordings and frame benches wait at each step for the fractures requested before it, and apply them in the order requested. A frame bench is a timed headless run of a scene. The live game still applies whatever is finished.

After that, the scene gave 1,266 members fallen and the same digest in a capture, a recording and a frame bench, run after run. A digest is a short fingerprint of where every body is, to the millimeter. If one body ends up a millimeter away, the digest changes.

A check for every change

On October 1, we decided to separate the engine from the game so it could be reused. The simulation, fracture and step code moved into engine modules in a series of steps. Each step had to leave the world exactly as it was.

We wrote tools/digests.sh for that. It runs 13 test scenes, replays two of our recorded play sessions, runs one scene stepping every other tick and another with sound on, and runs every scene as a screen capture. It prints a digest for each run and compares them with a stored baseline, using a release build with every environment switch cleared. A full run takes about 10 minutes.

A weapons range scene was added so the digests cover every weapon: the autocannon, rockets, the impulse cannon, charges and the gravity well, fired on a fixed script.

The check also catches changes that are meant. When fracture was changed to read where a body is, rather than where it was last drawn, 28 of the 40 runs changed and 12 did not. That change went in as a deliberate change with a new baseline. Scene 8’s demo ended with 1,690 bodies, against 2,068 before.

Headless mode runs a scene with no window, renderer or sound. Ten test scenes run that way ended with the same digests as the rendered app, in about a minute against about ten. That showed that nothing drawn on screen feeds back into what happens in the simulation.

One replay in seven

Replays of our recorded play sessions were part of the check, and two of them did not always repeat. A replay of a downtown session ended differently in about one run in seven, and a replay of a district-city session in one run in five.

The cause was the test runs themselves. Replays, frame benches, recordings and captures each brought their window to the front on launch, so whatever was typed in another app reached the simulation. One press of T changed a replay from the high quality tier to medium partway through. The run that ended differently once in seven had been running beside the live game.

Offline runs now show their window without taking focus, drop keys and clicks, and keep a quality tier given on the command line whatever the power state does. The test sends a power-state notification and a T press 600 steps into a frame bench. It still ends in the same state as a run without them, and three runs of the downtown replay end in the same state.

What it allows

Determinism is what makes exact replays, refactor checks and balance comparisons possible.

Provenance

How this post was made

Drafted by
A Claude Code session in the trilithon.games repository, October 10, 2026, working from the game's changelog, docs and tracker.
What it did
Drafted this post from the game's changelog and the digest script's description, and checked each number against them.
Reviewed by
Alex van Rossum,

On the roadmap: The engine

Linear MIP2-219, MIP2-298, MIP2-314, MIP2-320