25 September 2026 · 5 min read
Scores the server doesn't take on trust
The arcade's leaderboards don't believe the number the browser sends. They replay the whole run, on the server, and keep the score they get.
A leaderboard that accepts { score: 4210 } from the browser is a leaderboard for anyone who can open DevTools. For a while the arcade did what most do: it rejected the impossible (a score higher than a run of that length could reach) and trusted the rest. That stops the lazy cheat and nothing else. The plausible fake, a believable score typed in by hand, went straight through.
So the arcade stopped sending scores. It sends the run instead, and the server plays it again.
Send the input, not the result
While you play Asteroid Run, the browser records your steering: three whole numbers (the keys you're holding, and where the pointer is), 30 times a second. When the run ends, that recording goes to the server with the score you saw. The server feeds the recording into the same simulation the game uses and computes the score itself. The number you claimed is ignored; the replayed one is what goes on the board. Claiming 99,999 stores your real score.
That only works if the game is deterministic: the same input must produce exactly the same run, on a phone's browser and on a server in a data centre. Getting there took more care than the replay itself.
Making a game deterministic
- A fixed timestep. The simulation always advances in steps of 1/120 of a second, whatever the frame rate. A 144 Hz monitor and a struggling phone run the same steps; they just render them differently. Even the slow-motion shot when you leave a planet's orbit changes how fast steps are consumed, never their size.
- One source of randomness. Every rock, fragment and power-up comes from a seeded generator (mulberry32), never
Math.random. - No
Math.sin. The JavaScript spec lets each engine approximate trigonometry its own way, so Chrome, Safari and Node can disagree in the last bits. In a game where a rock clipping your wing ends the run, the last bit is enough to diverge. Free flight carries its own polynomialsin,cosandatan2, built only from addition and multiplication, which IEEE 754 makes identical everywhere.
A tape small enough to post
The recording is a list of samples, packed into a short string:
[keys held, pointer x, pointer y] every 4 steps (30 a second)
→ the pointer stored as the change from the last sample
→ a run of identical samples stored once, with its count
→ zigzag varints (small numbers, small bytes)
→ base64urlA keyboard player holds the same keys for long stretches, so their tape collapses to almost nothing. A mouse player's is bigger, but still a small POST.
The next hole: choosing the field
Replay alone isn't enough if the player picks the seed. You could record one good run, then search offline for a field where that same input survives longest, or replay a strong run on an easier field. So the server deals the field. When a run starts, the game asks for a seed, and the server returns it with a signed token: an HMAC over the seed and the time it was issued, valid for three hours.
When the run is posted, the token is checked, and the seed is marked used in Redis with SET NX, keyed to a hash of that run's tape. One dealt field goes with one run: the same tape can be posted again (under another name, after a clash), a different one can't. The daily field needs none of this, because its seed is the date, the same for everyone; the list of real asteroids it uses is checked against the one the server fetched from NASA that day.
What it rejects
- A claimed score: replaced by the replayed one
- A tape cut short, or run on a different seed than its token's
- No token, a forged token, or a token reused for another run
- A daily run with asteroids dropped from its list
Stack the Station works the same way with a simpler tape: a build is just its drop times, rounded to the millisecond, and the server drops the modules again. The cost of all this is discipline. Every rule lives in one simulation file that the browser and the server share, and a change that alters how a run plays out also changes what old tapes replay to.
By Miit Daga · miitdaga.dev
Next: Thirteen free models, one answer →