// Demo
Overrun
Sixty seconds in the arena. The chasers turn at a fixed rate, so a hard corner makes them overshoot.
Sixty seconds. You steer and you dash — the weapon aims itself at the nearest enemy. Every fifteen seconds the run freezes on a choice of two modifiers, and the bell drops you onto a score card with your rank, how long you lasted, and your own best run to beat.
The corner is the whole game
The chasers do not path. They steer — and they steer under a maximum turn rate, which is one call:
Vec2.turnVelocityToward(vx, vy, dx, dy, turnRate * dt, velocity);maxStep is radians per call, not per second, so under a fixed timestep it is turnRate * dt.
Everything that makes the demo playable falls out of that one bound. Run in a straight line and they
close on you exactly as fast as their speed allows. Cut a ninety-degree corner and they physically
cannot follow: they carry their old heading through the turn, swing wide, and have to come back
around. The prow arc drawn on each chaser points at the heading it is currently committed to,
which is what makes the overshoot readable rather than merely present.
That is also why the skill ceiling is not “move faster”. A speed modifier makes you harder to hit in a straight line; only cornering beats the turn rate.
What is engine, and what is ours
| Package | What the engine provides | What this demo writes |
|---|---|---|
@flare-engine/ecs | The World, archetype queries, and the movement / lifetime / bounds-cull systems the arena runs on. | Every gameplay system: steering, dash, contact, the bullet pass, and two-pass collect-then-act kills. |
@flare-engine/math | Vec2.turnVelocityToward — the max-turn-rate steering that IS the mechanic — plus SeededRNG, the weighted mod roll and the timed kill-streak model. | The radians-per-second to radians-per-call conversion, and the two degenerate cases the signature hides. |
@flare-engine/input | Keyboard action mapping, the W3C gamepad source, and the virtual-joystick maths. | The poll order, preventDefault, the stuck-key guard, canvas and DPR conversion, a radial dead zone, and thumbstick touch ownership. |
@flare-engine/core | Fixed-timestep GameLoop, the timescale knob the freeze writes, and Pool for zero-allocation rounds. | The hit-stop envelope, and a separate raw-time run clock — Clock.elapsed is scaled, so the bell would drift. |
@flare-engine/render | Camera2D follow with a dead zone, camera shake, and the motion-scale seam reduced motion drives. | The view transform itself — Camera2D never applies itself, and its x and y exclude shake. |
@flare-engine/render-web | The CanvasKit surface, the byte-accurate wasm loader, and the scene, composite and present frame. | The click-to-load gate and a one-way lost-context latch, because a restored context does not heal. |
@flare-engine/postfx | The frozen grade: one bloom pass and a chromatic split, shared with the five animation showcases. | Nothing — the chain is the site's existing one, unchanged, which is the point. |
@flare-engine/errors | ErrorBoundary around the island, so a CanvasKit Embind throw cannot leave a dead canvas on the page. | The fallback card the boundary renders. |
@flare-engine/physics | CollisionWorld with layer masks for player and enemies, and shapeOverlap for the bullet pass. | Symmetric layer and mask on every collider, and a bullet-speed cap, because there is no continuous collision. |
@flare-engine/particles | ParticlePool: capped, prewarmed death bursts with size, alpha and colour envelopes. | The burst shape and the drawing — the package renders nothing. |
@flare-engine/a11y | The reduced-motion state holder, plus motionScale and dampenedMotion — the binary kill and the soften-not-kill curves. | The OS matchMedia bridge, shared page-wide, and the four-lever policy: shake to zero, grade, burst and hit-stop softened. |
@flare-engine/scene | Menu to run to score, and the fade transition's phase and progress. | The three scene classes, the veil, and the in-scene modifier overlay — a pushed scene stops drawing the arena. |
@flare-engine/progression | The score-to-rank tier ladder — `resolveTier` turns a final score into D..S. | The five thresholds, and a floor so a score under the lowest tier still resolves to a rank. |
@flare-engine/storage | A versioned document store over IndexedDB, with sanitizers that never throw on a corrupt save. | The memory fallback, because the adapter rejects lazily and there is no localStorage adapter. |
Want to build one? Showcase: Overrun rebuilds it from first principles — including the three packages the first draft claimed and this one does not.