Zombies64 has two playable outputs and one source of truth. The .z64 is the game. The browser build is a fast, client-only presentation of the same round, movement, combat, door, and zombie rules.
That boundary matters. A browser can prove that the portable logic compiled to WebAssembly and produced the expected state. It cannot prove that the Reality Signal Processor accepted a display list, that an audio buffer survived contention, or that the cartridge behaves on the memory bus. Those are N64 questions.
One simulation, fixed steps
The game rules live in a freestanding C17 module. A step consumes an input command and advances exactly one 30 Hz tick. Rendering and audio observe the resulting state; they do not own it. That gives the host test runner, WebAssembly client, and native presentation the same state transition:
input → fixed simulation step → events → presentation
The order is deliberately boring. Spawn decisions happen before actors move. Damage resolves before the round transition. Presentation receives events only after the authoritative state is settled. A browser frame can arrive late without changing the simulation history.
The canonical tape is 720 ticks long. Host tests play the commands first. The same tape is then compiled into a finite native ROM and driven through Ares. Both must terminate at state hash add9d567919f0b49.
The short reel is an archived August 20 capture from an earlier native presentation build, remixed here with the score layers. Its pictured assets predate the September 8 Blender redesign. The browser screenshots elsewhere in the gallery are archived from August 23. Neither set establishes the redesigned assets’ appearance or performance; fresh rendering checks, machine-readable Ares telemetry and the terminal hash remain separate gates.
Native presentation stays native
The cartridge renders the theatre, animated stalker, M1911, iron sights, HUD, title, pause, and game-over presentation through Tiny3D and RDPQ. It mixes the score and event sounds through libdragon. It requires the Expansion Pak and targets a 320×240 RGBA16 framebuffer.
The native HUD is not a screenshot pasted over the browser. Score, vitality, ammunition, prompts, damage, and round changes are drawn from the live cartridge state. The same applies in the web build: it has its own Three.js presentation, but it reads the same compact C state rather than reimplementing the rules in JavaScript.
What each gate says
- Strict host and sanitizer tests catch logic errors and undefined behavior.
- The WebAssembly replay catches ABI drift in the browser build.
- The Ares smoke and TAS protocols catch boot, input, timing, telemetry, and terminal-state failures.
- Gopher64 is an independent compatibility check.
- Real hardware remains the release gate.
The browser player is useful because it is instant, static-hostable, and inspectable. It is not labeled an emulator because the available browser N64 cores did not execute this modern libdragon and Tiny3D ROM correctly. Failing honestly is more useful than putting a broken emulator behind a polished play button.
The full commands and pinning rationale live in the repository’s technology stack document. Every public build also ships the exact ROM as a download, so the cartridge artifact never disappears behind the port.
