Browser game · phone and desktop
SWARMGEDDON
Survive swarms of alien insects across three worlds. A twin-stick shooter with boss fights and a daily challenge.
- Role
- Timeline
- Stack
- Links
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
- 1 · Every frame request
Animation Caps the elapsed frame time before adding it to the accumulator. A stall cannot queue seconds of simulation steps.Frame -
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 · 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
-
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.
-
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.
-
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.
What’s next
Ship the iPhone version from the same codebase.
Try it
Shared approaches
Make runs reproducible with seeded randomness
Invicto, Flawless, The Veil · El Velo, Conuco, Coherente, This site
Keep rules separate from the interface
Tune difficulty with simulations
Invicto, Flawless, The Veil · El Velo, Coherente
Protect saved progress
Conuco, Coherente, El Corre Corre, Invicto