The browser can say anything happened. We only believe our own replay.
A browser-side Top 10 is one line of code and completely worthless, nothing stops a client from just claiming a bigger number. The fix here isn't a smarter check. It's not trusting the browser's score at all.
Wanted: an online Top 10. The usual problem with that, in any browser game, is that the browser is not a trustworthy witness, it's running on a stranger's machine, and anything it tells the server about "what happened" can simply be a lie. Most small games either skip a real leaderboard entirely or ship one that anybody can beat with the browser's dev tools open.
Why this game can get away with replaying it
The core rules are fully deterministic: the same starting config, the same random seed and the same sequence of key-presses always produce exactly the same game, every time, on every machine, that's a tested property, not an assumption. Which means a finished game is nothing more than its own recording, and the recording is something anyone holding the real rules can play back and check for themselves.
What actually gets sent
The browser records every tick's held keys as it plays, a short, compressed string, nothing more. Only when a run is good enough to reach the Top 10 does the page send anything at all: a name, a signed run token, and that recording. The server then runs the exact same core rules against the recording itself and keeps whatever score its own replay produces, never the number the browser reports.
Around that one idea sits a small ring of defences: the run token is single-use and carries the seed the game was actually started with, so nobody can hand-pick a favourable seed after the fact; a game of T ticks has to have taken at least T divided by 60 real seconds since its token was issued, so a script can't fast-forward a submission; requests are rate-limited per address; and names go through a short filter.
The bug that had to be fixed first
Determinism across different JavaScript engines turned out to need one real fix before any of this could work. The rule for where a new piece spawns originally used two trigonometric functions, and the language specification explicitly allows different engines to round those slightly differently. A landing exactly on a diagonal could spawn the next piece in a different cell in one browser than on the server, meaning a perfectly legitimate score could get rejected simply because two honest implementations of the same spec disagreed by a fraction of a floating-point unit. That spawn rule is now integer-only: compare squared distances, tie-break to the earlier cell, tested including the exact-tie case. Everything else in the core sticks to arithmetic that every engine is required to agree on exactly.
What this doesn't solve
Said plainly, because it's true: a program that plays well in real time, at normal speed, cannot be told apart from a person doing the same thing. Nothing here claims otherwise. What it does guarantee is narrower and still worth having, a submitted score is never anything other than the score this game's actual rules produce from the actual inputs recorded, because the server is the one playing the game back, not taking anyone's word for it.