wanessalabs games production July 27, 2026

Paper Squadron

An on-rails origami shooter on a hand-rolled canvas software rasterizer, zero 3D libraries. Fly a paper plane through daily levels, rings, and 30 bosses.

TypeScriptReact 19HTML Canvas 2DWeb Audio APIViteCloudflare Pages
Paper Squadron screenshot 1

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.

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.

Development timeline

1 logged update on 27 Jul 2026.

  1. Fly a paper dart into an origami dogfightlaunch

    Shoot down shuriken, cranes and turrets on the way to a paper dragon with one weak point. A new level every day.

Written from the release notes published with each update, not afterwards. See every project →