Every sprite on this page is text until it is drawn — a grid of characters plus a five-colour palette, rasterised to canvas at runtime. Forge new enemies, take apart the animation, then fly the thing.
Six hand-authored sprites, 8×8 to 12×13 pixels. Each row is a string; each character indexes a palette entry; a dot is transparent. Open the source on any card to see the exact data the renderer eats.
Procedural generation: a seeded RNG fills half a grid with a centre bias, mirrors it, walks the edges to lay down an outline, then shades every cell by whether the pixel above it is empty. Palette is one random hue with a four-step luminance ramp and a complementary accent for the eyes. Pick one and it flies in the game.
Four techniques the game runs on, isolated so you can see each one working on its own. Every panel is an independent canvas driven by one shared requestAnimationFrame loop.
Three tail frames swapped on a fixed clock. Drop below ~6 fps and the flame reads as three separate pictures instead of fire.
Particles inherit the exploding sprite's own palette, so debris always matches what died. Gravity, drag, and a shrinking square instead of a fading circle — fades look wrong next to hard pixels.
The curve is the character. Linear reads as machinery, ease-out as something with mass, back-out as something alive. Hunters in the game use ease-in; the pickup bobs on a sine.
Three star layers at 0.35×, 0.7× and 1.4× scroll speed, dimmest and slowest at the back. Costs nothing and is the entire reason the game feels like it is moving.
All of the above, assembled. Waves escalate every 20 seconds; asteroids take three hits and never shoot; drones drift and return fire; hunters commit to a dive and cannot correct.