2026-08-30, written at the second parking. The project is a port of the driving screensaver from Zig to Codex — 26 chapters, 5,701 lines of port, 3,098 lines of checks, 17 graded seams, 50 commits. It runs in a browser. The essay is about what that does and does not prove, and about the two things worth doing next, which happen not to collide.
A 728 KB wasm module, computed entirely in Codex, driving the driving game's own
unmodified blitter.js — symlinked rather than copied, fetching the same
absolute /driving/safari.wasm path the real game fetches. The road, its corners,
the conifers, the intersection towers, the guard rails, the pond, eight species of
animal, a cat that crosses and leaps, a truck that brakes into corners and lights
the road at dusk. 2,430 draw commands in the opening frame.
It looks like the game. That is the claim, and it is the one that was in doubt when this started: not whether Codex could compute geometry, but whether a real interactive program — one somebody would actually watch — could be carried across and still be the same thing at the far end.
Every file in games/driving/wasm has a chapter. Twenty game files, twenty-six
chapters — the difference is where the port found structure the original had left
implicit, and split it.
The eye test is the deliverable and it is also the weakest possible evidence, which is why almost all the work went into the other kind.
Seventeen checks in judge/, each grading a seam where the game computes something
a probe can read. The probes @import the real, unmodified game module, so the
hand-written Zig is the oracle and never a copy of it. The whole sweep is 6.5
seconds warm, ninety seconds cold, and it prints GREEN or RED.
Behind that, three arms — the game, the port through the zig plug, and the same
Codex source through the Codex compiler's own x86-64 emitter as a kernel image
under QEMU. All seventeen checks agree on bare metal. The five spike entries agree
bit for bit on 523,414 IEEE-754 patterns, which became possible only this week,
when the plug learned real-to-bits and coordinates stopped going out as scaled
decimals.
I wrote about the limits of that apparatus separately — the three arms of safari — and the short version is that redundancy is blind to everything upstream of where the paths fork, which this project proved the hard way by finding a defect both arms shared.
Almost nothing difficult was about drawing. The interesting difficulties were three.
The toolchain had holes exactly where a real program leans on it. Codex Real
had no conversion to Integer at all when this started, so the port could not report
a computed number, let alone round one. Filling that took two emitter rows; the
bitcasts took two more. Both were declared in the language and tested in its own
test suite, and simply had no zig emitter — a shape you only discover by writing a
program big enough to need them.
Calibrating a tolerance is a research task, not a constant. The port computes in f64 where the game computes in f32, so every value differs somewhere in the last bits, and every gate had to be measured rather than chosen. The loosest is 8e-5 relative, and it is set by a single vertex: a tree trunk projected 270,939 pixels off the side of a 960-pixel canvas, where the perspective divide multiplies a micron of depth disagreement by five million.
Some differences are the port being right. The depth sort is the sharpest case.
render.zig insertion-sorts a mutable array; carried literally onto persistent
Codex lists that goes cubic in allocations and exhausts four gigabytes. A stable
merge sort produces the identical order — and stable is load-bearing, because two
depths closer than an f32 ulp are the same number to the game and orderable to the
port. A rail post and a rail bar swapped places in a 3,091-command stream before
that was understood, and no per-module check could see it, because both modules
were right.
The port will never be bit-identical to the game, and this week that stopped being an assumption and became a measurement. The whole ride finishes in 6,960 frames where the game takes 7,000. Running the game's own f32 code against itself: a one-ulp perturbation at the start is absorbed completely, a persistent relative bias does nothing at 1e-8 — and at 1e-7, which is f32's own resolution, the total swings to 7,061 or 6,971. The ride's length is not a property of the model; it is a property of evaluating the model in f32. The port sits inside the family the game produces for itself.
The driven page is off every arm. The checks grade seams and the spike entries
grade stills at fixed route positions. DriveMain's frame loop and its wasm shim
have no witness at all, because that program's output is a wasm module rather than
a line of text. It is the largest uncovered surface left and the fix is known
rather than novel.
The whole-frame check grades two states. Chosen as branches — a hard lean, and a long straight with the truck close.
The seam is already cut, and it was cut for other reasons.
NOTES §5 decided at the outset that the blitter and the rasterizer stay on the
far side — the port computes a draw-command buffer and hands it over, and
everything downstream of that is somebody else's problem. That decision was made to
bound the work. It happens to be exactly the boundary a GPU backend wants.
The buffer is a flat, typed, per-frame list: a tag, a colour, a strength, a point
count, then coordinates, with the gradient tags trailing a second colour and their
own geometry. It is already the thing the whole-frame check grades command for
command, and already the thing the third arm compares bit for bit. Nothing in
port/ should have to change — the buffer is the API, and a shader consuming it
is no more exotic than a Canvas 2D context consuming it.
What needs deciding is how much of the blitter's own shading maths moves across.
There are four gradient forms in play: the horizontal round gradient that gives
cones and trunks their volume, the bull's two-stop linear and radial gradients, and
the truck's headlight radial with alpha in both stops. harness/spike_svg.py
already reimplements three of them approximately and says in its own header exactly
where it is not faithful — "if a spike and the browser disagree about a colour,
believe the browser" — which makes it a decent map of the territory and a bad
specification.
The other question is whether a GPU path wants the buffer at all, or wants the scene one level up. The buffer is already flattened and depth-sorted on the CPU; a GPU could take the depth sort itself. That is a real design choice and not one to make from here.
Eight, written up, none sent. They came out of ordinary use — writing a program, not hunting for defects — which is the only reason they are interesting.
Two are silent wrong answers, which is the worst category a toolchain has. A
declared trapping multiply is emitted as a wrapping one, so 4000000000 *
4000000000 returns a plausible negative number with exit status 0. And a Real
literal wider than an i64 is read as a different number: written out longhand,
1e18 arrives as -8.4467e17, silently. That second one is the front end rather
than any plug — both arms print the same wrong constant, and the value reaches the
emitter as a bit pattern with the digits nowhere in the file — and it was found
because it corrupted our own oracle, making a check red at a value the port had
computed correctly.
One is a test that cannot fail. Math chapter Cordic documents ~0.1% accuracy and
delivers 0.45%, and its entire test is
Chapter: FwdCordicTest
cites Math chapter Cordic
Section: Entry
opening : [Console] Nothing = act
print-line-uni "Math/Cordic OK"
end
It cites the chapter and prints a string. It passes whether or not Cordic works.
FINDINGS.md now opens with how each of the eight should travel, because the
routing is the part that is easy to get wrong: two are small mechanical PRs, one is
an offer rather than a defect (there is no Real arc tangent anywhere in the
foreword's thirteen directories, and ours is measured to 1e-9), two are issues
rather than patches because the fix is a policy call we should not make from
outside, two want investigating first, and two belong to angry-gopher — one of
which is a one-line buffer fix already on a live path.
They do not touch the same things. (a) lives in web/ and downstream of the
draw-command buffer; it needs no Cobblestone change and no port/ change. (b)
lives in Cobblestone and angry-gopher and needs no browser at all.
They also fail differently, which matters more. The GPU work is exploratory — nobody knows yet how much of the blitter moves — so it will spend time in states that do not render. The findings work is finite and well-specified: eight items, each with a repro, and the queue empties. Running an open-ended task beside a bounded one is a better use of two attentions than either alone.
It was proposed as a port and it has been, mostly, a study of how you know a port is right — and of how much of that knowledge has to be measured rather than reasoned.
The pattern recurred so often it stopped being surprising. A tolerance that seemed
principled was 17× out until someone computed r^13/13!. A chapter's docstring
said the foreword had no join and the foreword had a join. Entries were split twice
to fit a heap that was never the constraint — the printers were allocating a
quadratic amount, and an allocator that never frees is very good at hiding an
algorithmic mistake behind itself. A claim that two compilers agreeing on a
knife-edge computation proved something deep turned out to be wrong by eight orders
of magnitude, and the data refuting it was already in the same file.
Every one of those was found by running something rather than by reading it. The port passes the eye test, and the eye test is the deliverable — but the reason to believe it is that seventeen checks, three arms and 523,414 exact values were willing to say otherwise and did not.
~/showell_repos/safari-codex, pushed to showell/safari-codex. README.md
orients and its "Where to pick this up" is written for a cold start;
PORTING_NOTES.txt is sixty-one numbered notes; FINDINGS.md is the eight and
their routing; PLUG_WORK.md is the emitter work and its lineage.