Skip to content
Work

Browser game · phone and desktop

Live swarmgeddon.adelsonaguasvivas.workers.dev

SWARMGEDDON

Survive swarms of alien insects across three worlds. A twin-stick shooter with boss fights and a daily challenge.

SWARMGEDDON on a phone: a STAMPEDE alert appears, followed by a swarm of alien insects as the pilot fires.
Playing with touch controls, two and a half minutes into a run.
Role
Solo: design, code, release
Timeline
Jun 2026 to now
Stack
TypeScript, PixiJS, WebGL, Web Audio, Capacitor, Cloudflare Workers

Live at swarmgeddon.adelsonaguasvivas.workers.dev. Public code on GitHub. Status checked Oct 22, 2026.

By the numbers

60 Hz

fixed simulation step with an accumulator; rendering interpolates between steps

7

seeded random streams: spawn, script, boss, loot, draft, combat and effects

30

seeds per world for each bot type in the final balance matrix

14

weapons, plus 31 perks and 10 fusions, defined as data

Fig. 1 One frame: fixed steps, then one render
  1. 1 · Every frame requestAnimationFrame Caps the elapsed frame time before adding it to the accumulator. A stall cannot queue seconds of simulation steps.
  2. 2 · Simulation, 60 Hz

    Fixed steps Runs one step for every 1/60 s in the accumulator, subtracting that time after each step. Game state changes only here.
    7 seeded streams Each uses mulberry32 with a seed derived from hash32(run seed, its salt).
    • spawn
    • script
    • boss
    • loot
    • draft
    • combat
    • fx
    Simulation purity check Every build rejects calls to these APIs in src/systems, src/game and src/content:
    • Math.random
    • Date.now
    • performance.now
    • window.
    • document.
    • localStorage
  3. 3 · Once per frame Render Interpolates between the last two steps using alpha = accumulator / step. The simulation stays the same at 30 fps and 120 Hz.

The idea

In SWARMGEDDON, you fight swarms of alien insects across three worlds, with bosses and a seeded daily challenge. This twin-stick survival shooter runs in phone and desktop browsers. I built it without a general-purpose game engine, aiming to keep dense fights responsive on a phone.

What I built

The simulation advances at a fixed 60 Hz using an accumulator. The renderer draws once per frame and interpolates between steps. I wrote a spatial hash for collision checks instead of using a physics engine. Seven mulberry32 random streams derive from the run seed, so the same seed and inputs reproduce the same run.

Weapons, perks, enemies, pilots and worlds are defined as data: 14 weapons with 9 evolutions, 31 perks, 10 fusions, 3 pilots and 3 worlds. The Web Audio API synthesizes every sound, so the repo contains no audio files. Capacitor packages the same build for iOS and Android, with saves backed by native storage.

The hard parts

  1. 01 Same seed, same run

    Replays and fair daily challenges depend on a reproducible simulation. Every build scans the simulation folders and rejects clock reads, unseeded randomness and browser or audio calls: Math.random, Date.now, performance.now, window, document, localStorage and audio APIs.

    Where in the code: scripts/check-sim-purity.mjs (opens in a new tab)

  2. 02 The same game at any frame rate

    A 30 fps phone and a 120 Hz monitor must follow the same rules. The simulation stays at 60 Hz while the renderer interpolates between steps to keep movement smooth.

    Where in the code: src/core/time.ts (opens in a new tab)

  3. 03 Tuning with bots

    Bots play the real game in a headless browser using turret, roam, crude and smart policies, plus dash and reaction-time variants. A matrix runner records results by world and seed. I used those measurements to tune boss health, fight length and swarm density.

    Where in the code: scripts/playtest/matrix.mjs (opens in a new tab)

What’s next

Ship the iPhone version from the same codebase.

SWARMGEDDON level-up screen: three perk cards, with reroll, banish and skip options below.

Try it

Shared approaches