The game was not a project, it was a birthday present. A firefly you fly with your finger, candles to blow out by passing right through the flame, a chest at the end of the journey. Given as a link, single use, then put away.
Today it runs on this site’s 404 page. Between the two, I added no features. I removed half of them.
What a game has to lose in order to live elsewhere
The original version has a menu, two game modes, a victory screen, a service worker that installs it as an app, a manifest and its icons. All of that serves the gift: you open it, you choose, you win, you take it offline into a garden.
On an error page, each of those pieces becomes a flaw. A menu asks a decision of someone who was looking for something else and found nothing. A victory screen promises an ending to someone just passing through. And a service worker declared at the root would hijack the whole site’s navigation, 404s included: it would delete the very page hosting it.
So the embedded version has no menu, no modes, no ending screen, no service worker. You land on « Tap to fly! », and that is all.
A guest, not an owner
The rest of the work consists in making the game polite. It does not touch the tab title. It does not take focus on load. Its styles are all prefixed by a single class, and I counted: three injected rules, none of them spilling onto the page. It writes two keys to local storage, the high score and the sound state.
On the portfolio side, nothing moved either. No dependency added, no build step. The bundle and its nine sounds are static files, versioned like images, served on an absolute path. That detail is not cosmetic: a 404 can answer on any address, and a relative path would have gone looking for the sounds next to that address.
The height that chased its own tail
The game computes the size of its canvas from that of its container. I had given that container a minimum height, min-height: 100dvh, which seemed enough.
It was not. The grid row was sizing itself on the canvas while the canvas was sizing itself on the row, and the two swelled together: 1241 pixels of section for an 860 pixel screen. A minimum height is not a measurement, it is a promise. Anything that has to be measured needs a defined height, not a floor.
The space bar confiscated
The game reserves the space bar for its brake. Phaser listens for it on window and cancels the browser’s default behaviour, without looking at who has focus.
On the 404 page, the result is clear: impossible to type a space in the contact form, and no way to activate a button from the keyboard. The fix comes to ten lines, re-read on every press, and the rule is simple: if focus is anywhere other than the game, the key belongs to the page.
That is damage no screenshot shows. A guest component that listens to the whole window does not stay in its corner, and the integration is only finished the day you have checked what it takes, not only what it gives.
The framing, which was not a width problem
The canvas is in portrait. It sizes itself on the smaller dimension of its frame, so it only grows by gaining height, never width. The title set above it was eating 230 pixels of that.
Moved beside it, on the same line, the game gained 1.6 times linearly and two and a half times in area. Not a line of the game’s code changed. What looks like a width problem is often a height problem.
What I take from it
A 404 page is a page you put up with. It might as well give something back.
And a well-made gift has a second life, provided you accept stripping out what made it complete. The menu, the two modes, the victory screen: everything I was pleased with, and that the page next door had no need of at all. What is left fits in one file and nine sounds, and is played with a finger.




