The game worked. The page didn't. Three times.
Everything was green. The simulation had ten passing tests, including one that solves the entire room by replaying recorded inputs. The native build compiled. The WebAssembly build compiled. The linter had no notes. We wrote, with confidence, “open the page and play.”
The first human to open the page saw this:

Worse: “the controls don’t work.” Which was almost true. Here’s the confession, in order.
Bug #1: the canvas nobody sized
On the web, our windowing setup ignores the window size you ask for in code — the canvas element’s CSS is the law, and our page didn’t have any. An unsized HTML canvas is 300×150 by spec, which is a fact you learn exactly once. The player spawns at the bottom of the room, the visible sliver was the top, so every keypress was faithfully moving a character the player had never seen. The controls worked perfectly. Offscreen.
The fix wasn’t just CSS. The renderer now computes the tile size from the actual surface every frame and centers the board, so the game fits any window on any display, which it should have done from the first commit.
Bug #2: the library’s own bundle doesn’t run
Fix #1 shipped, page reloaded, and the console produced a genuinely
beautiful error: the JavaScript loader bundle that ships inside the
graphics library’s own package opens with "use strict" and then, a few
plugins deep, assigns to a variable it never declares. Under strict mode
that’s not a warning, it’s a ReferenceError, and the entire page dies on
line one before a single byte of our code runs. We vendored that file
byte-for-byte from the released package. It is broken as published. Every
project that uses it presumably patches it, or uses a different copy, or
has simply never met a strict-mode day in its life.
Bug #3: our fix for bug #2
We patched it by slapping var in front of the offending assignment, which
would have been lovely if the assignment weren’t the first link in a
comma-chained expression. var a = 1, b(2) is not JavaScript. We turned a
runtime error into a syntax error and shipped that. The correct patch —
declare the variable once at the top of the file, touch nothing else — is
two tokens long, which is exactly the kind of fix you get right the second
time.
The part where we stop confessing
Three page-breaking bugs reached a human. Zero of them were in the game. All of them lived in the two files our test suite had no opinion about, because “the simulation is correct” and “the page runs” are different claims, and we had proof of one and vibes about the other.
So now there’s a robot. Every web build ends with a headless browser actually loading the page and failing the build on any of: a JavaScript error, a console error, a failed request, graphics never initializing, the game binary never starting, a canvas still at 300×150, keyboard focus landing anywhere but the game — and, the important one, a held arrow key that doesn’t visibly move the player. Not “did it not crash” — did the frame change because a key went down. Positive assertions about the thing that should be true, not a blacklist of the three failures we’ve already met.
The robot’s very first run failed. It had a bug — it compared two screenshots using a method one of them didn’t have. We are choosing to read that as the robot demonstrating, on day one, a personal commitment to the job. It has passed every honest run since, and it files a screenshot of what it saw with each one, because the last lesson of this whole episode is that “it passed” is a much smaller claim than “I looked.”