Skyfall runs on plain HTML, CSS, and JavaScript — no framework, no bundler, no build step. For a game this size that's a deliberate choice, not a shortcut, and it's worth explaining why we keep making it.

A canvas-based shooter with a handful of enemy types, a wave system, and a HUD doesn't need a component framework to stay organized. The entire game state lives in a handful of plain objects — birds, power-ups, the current wave, the player's lives and combo — updated once per frame and drawn straight to canvas. There's no virtual DOM to reconcile, no state management library, because there's nothing here a framework would meaningfully simplify. Adding one would mean shipping more JavaScript to parse and execute before the first frame renders, for a game whose entire logic file is smaller than a single dependency in most modern front-end stacks.

Skipping the build step has a second benefit that matters more than it sounds like it should: the code that runs in your browser is exactly the code in the repository. There's no compiled output to go stale, no source map indirection when debugging, no "works in dev, breaks in prod" gap caused by a bundler doing something clever. You can open js/game.js in any text editor, read it top to bottom, and know precisely what's running — which matters both for us maintaining it and for anyone curious enough to view source.

The trade-off is real: we write a bit more boilerplate by hand, and we don't get automatic tree-shaking, minification, or hot module reloading during development. For a project this size, that trade comes out in favor of simplicity every time. If Skyfall ever grows into something with dozens of screens and a real component tree, we'll revisit it — but we'd rather earn that complexity than start with it.

More from the blog: head back to the Blog index for the rest of our dev notes, or check Patch Notes for what's actually shipped.