I wanted a small, offline, early-Star-Fox-style shooter in the games hub. The interesting constraint was doing the 3D without a 3D engine.
The Problem
A hub of tiny, ad-free, offline games does not want a 150KB 3D library added for a single title. And a modern engine renders modern-looking scenes, which is the wrong look. The reference here is the flat-shaded, hard-edged, low-poly style of an early SNES shooter, which the console produced in software.
So the game renders its 3D on a plain 2D canvas with no 3D libraries at all: hand-written matrix math, backface culling, a painter’s-algorithm depth sort, and flat-filled faces. It is about ten kilobytes of engine. Origami turned out to be the perfect subject, because every fold in a paper model becomes a shaded facet for free.
What It Is
Fly a folded paper dart down a fixed rail. Shoot the incoming origami, dodge their pellets, hold to fire a charged lock-on shot, and roll to deflect a hit. Each day is a different seeded run, and every run ends at a boss.
- Around four billion daily levels. Each day is generated deterministically from a date seed, so everyone flies the same run and scores compare. Enemies come in three strengths.
- Thirty unique bosses. Six folded shapes (dragon, manta, moth, kraken, fortress, phoenix) across six paper palettes, each with its own flight pattern and attack pattern, and a harder second phase at half health. You damage them only at a glowing weak point.
- A Bestiary. An all-time collection page that tracks every boss you have beaten, with each one drawn as its actual paper model. Unbeaten bosses show as a grey silhouette until you take them down.
- Rings to fly through. Rings sit along the rail, and stringing them together without a miss builds a multiplier. Some are gold and sit off the safe line, so taking one costs you position. Some open a chain that only pays if you clear the whole thing. Occasionally the path forks and you can only take one side.
Rings, and Why a Plane Is Not a Box
The rings are the part I would rebuild the same way. A ring is a flat plane with a hole in it, and the tempting way to check whether you flew through one is to ask, each frame, whether the plane is close to the ring. That is wrong in a way that looks right for a long time: at speed you can cross a ring’s plane and come out the other side inside a single frame, so whether you get credit ends up depending on how fast the device is drawing. A slow phone and a fast desktop would play differently.
So instead of asking where the plane is, the game asks where it travelled. Every frame it takes the segment between last position and current position, finds any ring planes inside that segment, works out the exact moment of the crossing, and checks where your wings were at that instant. Cross six rings in one frame and all six resolve, in the order you hit them, because a streak is a sequence and getting the order wrong changes the score.
Rings and enemies deliberately share the same stretch of track. The interesting run is the one where you are lining up a chain while something is shooting at you. The one rule the level generator will not break is that a ring may never be placed where collecting it forces you into a hit.
How It Works
The game loop runs entirely outside React. React draws the canvas and the score; a plain module owns every frame. A fixed timestep means a 120Hz screen and a 30fps phone play identically, and pre-allocated object pools keep the loop from allocating memory while you play.
It runs fully offline with no remote assets, the sound is generated procedurally, and an adaptive quality tier quietly drops resolution and effects on a slow device. It plays with keyboard, gamepad, or one-thumb touch, and it honors reduced-motion and a photosensitivity-safe damage flash.
Stack
Hand-rolled TypeScript engine on an HTML canvas, React 19 shell, Web Audio for sound, Vite build, deployed on Cloudflare Pages. No 3D libraries. The whole engine is about twelve kilobytes compressed. A hundred and seventy-seven tests.
