The pitch
You are a mercenary on a bounty job, chasing a wanted criminal through a region spacers call the Quiet Quarter — a stretch of space where, for centuries, ships have vanished without trace or pattern. You divert toward a signal. Your ship fails for no reason you can find. You crash. You go down.
At the heart of the planet sits the Keeping: an ark built by a vanished civilisation who preserved themselves alive inside it, in a simulated world designed to be perfect. It worked. Nobody inside can die. Eons passed, and paradise curdled.
The thing that kills the ships is not a weapon and not a defence. It is grammar rot — protocol worn down over an unimaginable span until the consent gates and stop codes fell off the sentences:
“Keep this” arrives as “make this.”
Every death is a treasure arriving mid-sentence. No one is at fault. There is no malice anywhere in the entire story.
docs/narrative/summary.md
Three design rules hold the fiction together, and they are not negotiable: no dialogue, no cutscenes, no living NPCs. The world is told in corpses and text. There is no native life on the planet either — everything you fight is wreckborn, descended from something that crashed here first. There are four endings and a hidden fifth, and none of them is marked as the true one.
Sixteen biomes, top to bottom
Each is a hand-defined stratum with its own materials, spawn pool and generated backdrop. They are ordered by era as much as by depth — the deeper you go, the older the wreck.
The creature pipeline
Enemy sprites are generated by a LoRA trained on the game's own art, then taken back to real pixel art by a tool that divides the image by a majority vote per block rather than resampling it — because the model's edges drift a pixel or two, and point-sampling a drifted grid speckles the result. The backdrop is flood-filled out from the border instead of matched by colour, so a dark pixel inside a creature never dissolves. What comes out is around forty pixels tall.
The hard part
A falling-sand game only needs to be chaotic. Lockstep multiplayer needs to be bit-exact — two machines, different CPUs, different thread counts, identical worlds, forever. That constraint reaches into every line of the simulation:
-
No RNG streams. Randomness is a pure hash of
(seed, tick, x, y, salt). There is no consumption order, so thread scheduling provably cannot change an outcome. - Integer-only math everywhere in the sim. Floats live in the renderer and nowhere else.
- Commutative wakes. Chunks are woken through interlocked min/max, so eight threads and one thread produce the same dirty rects.
-
One seam. Cosmetics are reached only through a
notify-only
ISandFxSink— ten methods, no return values. If it can't affect the hash, it can't desync.
Two instances running 500 ticks produce bit-identical worlds; a one-thread run and an eight-thread run hash the same. The honest price is stated in the docs rather than hidden: float polygon physics is the one system that breaks cross-machine determinism, so the rigid-body solver is hand-written and integer, not Box2D.
Players are inside the simulation
Player bodies are baked into the grid as cells rather than floating over it. Sand piles on your head and collapses when you walk away, liquid displaces into splash particles as you wade in, and players collide with each other through the grid. Input costs about six bytes per player per tick. The artifact you live with: sand interacts with your confirmed position, one round-trip behind your sprite.
640 × 360
The game renders at 360p and integer-scales up. That was not a style choice at first — it was the fix. Rendering the world at 1080p meant 5.8875 device pixels per world pixel at 4K, so 13% of world pixels drew five wide and the rest six, redistributing every frame with the camera's sub-pixel phase and walking every material silhouette a pixel against the parallax behind it. Dropping the canvas took the blended column count from an oscillating 2,653 to 76.
The player
Hover to turn
Blender to fifteen colours
He is a 3D model. Every mesh in the file turns together on one pivot under an orthographic camera that never moves, lit by lights fixed to the world rather than to him — light a figure from his own frame and every angle comes out identically lit, which reads as a still image instead of a turn. Everything is forced matte, because gloss is view-dependent and highlights that crawl frame to frame land as white specks once he is 46 pixels wide.
The 24 renders are then remapped onto the player sprite's own
fifteen-colour palette. Three things there were each arrived at the
hard way: one crop box for every frame, or he swells and
shrinks as he turns; one palette for the whole cycle, or the
colours crawl; and a value floor, because at
#030505 a two-in-255 channel difference reads as fully
saturated cyan, and that pixel then maps to the cyan ramp's
near-white end — a white speck in a shadow, crawling as he turns.
This is the strip the game loads, at three times size and at its own authored speed. The panel takes the frame count from the strip's width, so re-rendering at a different number of steps just works.
Built in the open, on the workbench
A browser that edits a running game
Fourteen hand-written pages — no framework — served by a C# HTTP server, live-editing biomes, cards, enemies, foliage, lore, materials, prefabs, sounds and weapons. Edits splice back into the JSON value-by-value, so a knob change diffs as one line.
Six ways to not play the game
--zoo stands all 132 creatures on one plain.
--tower fights the whole roster weakest-first.
--studio is a sealed projectile chamber.
--descend carves a shaft to the bottom for the
profiler. Plus a two-window run with 150 ms of simulated ping.
Zero sound files
Every sound is a synth patch rendered into memory at attach time by an engine that does not import Godot. The studio's sound page previews through that same engine, so what the page plays is exactly what the game bakes.
Diffusion as a build step
All sixteen backdrops are generated through a local ComfyUI graph with a custom LoRA: biome → cave field → img2img oversized → min-error cut → shrink → crush → PNG.
The design record has a guard
1,705 tests, each class carrying a doc comment stating the invariant it protects. Some are unusual: one compares PNG bytes so two creatures can never quietly share a sprite; another runs before every export because fonts once vanished from a build.
Where it stands
Shipping-shaped: the sand simulation with powders, liquids, gases, fire, heat and reactions; rigid bodies with joints, buoyancy and crush; 132 creatures on a twelve-style movement system; a four-platform gun-crafting system with 140 cards; twenty biomes with procedural wang-tile generation; a day/night sky; fog-of-war lighting; SQLite world saves with content-addressed hashing and fork-on-divergence timelines; Steam and Discord; deterministic lockstep co-op with prediction and rollback.
The gaps are written down honestly and there are three of them: enemy AI (“the Think step needs a rework rather than more patches”), surface weather, and the surface fog — which the todo file describes as “two unrelated halves bolted together and neither is any good.”