Skip to content

Latest commit

 

History

8 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SWARM

A bullet heaven in one HTML file. No dependencies, no build step.

Move. Everything else shoots itself. Survive to 3:00, kill what shows up.

Five weapons that fire on their own, nine passives, five enemy types and a boss that will not let you outrun it. Commit to a build and your weapon evolves into something meaner. Crits, knockback, and a rare heart drop that the magnet deliberately ignores — the only thing in the game that ever pulls you toward the swarm.

Generated by Claude (Opus 5) in Claude Code — design, code, balance and documentation. The constraints are deliberate: one file, no dependencies, no art pipeline. Shapes, particles and ~20 lines of WebAudio do all the work.

The Swarm Mother at 3:05

Run it

Open index.html in a browser. That's the whole toolchain.

Main menu

Controls

  • WASD / arrows — move (this is the only input the game has)
  • 1 / 2 / 3 or click — pick an upgrade on level up
  • P / Esc — pause
  • R — restart after death or a win
  • T — settings and live tuning
  • M — back to the main menu from a finished run

Test it

  • index.html?test — 32 logic asserts, in the browser
  • node run-tests.js index.html — same asserts, headless
  • node render-test.js index.html — every draw path against a stubbed 2D context
  • node sim.js index.html / node many.js index.html — headless balance runs with a bot player
  • sh e2e-run.sh — end-to-end in a real browser: keyboard, mouse, settings panel, pause, restart, localStorage (needs Edge or Chrome)

many.js is the one that matters for tuning:

$ N=40 node many.js index.html
sloppy/random   win   4/40   median run 192s   median lv 14   evolved  1/40
decent/random   win   8/40   median run 210s   median lv 14   evolved  0/40
decent/smart    win  22/40   median run 197s   median lv 20   evolved 14/40
perfect/smart   win  22/40   median run 196s   median lv 21   evolved 12/40

Median run ~195s against a boss that lands at 180s, so nearly every run reaches the boss and the boss decides it.

The column that matters is how you spend level-ups, not how well you dodge: random picks win 8/40, focused picks win 22/40 off the same motor skill. Evolutions follow the same line — a committed build evolves in about a third of runs, a scattered one basically never. That is the intended shape. Batch-to-batch variance is wide, so compare profiles within one run of many.js, not across runs. N=40 for a sample worth trusting; the default 12 is for a quick look.

Upgrades

Every level gives three cards drawn without replacement from everything currently legal — unowned weapons, owned weapons below max level, passives below max level. Levels queue rather than interrupt, so a big gem haul hands you several picks in a row.

Evolutions are the reward for committing to a build. Take a weapon to level 4 while its paired passive is at level 2, and an EVOLVE card appears — and keeps appearing until you take it, because an evolution left to the same random roll as everything else showed up in 2 runs out of 40. Evolving consumes the weapon's slot rather than adding one.

Weapon pair with becomes
Bolt Multishot Railgun — a lance that pierces the entire screen
Scatter Sharpen Flak — a wall of piercing shrapnel
Halo Bloom Event Horizon — a wide ring of heavy orbs
Nova Overclock Singularity — twin screen-clearing shockwaves
Lash Sprint Reaper — a full circle sweep, every time

Level up

WEAPONS and PASSIVES are the whole content pipeline: add an entry and it shows up on cards, in the HUD chips and in the tests automatically. See docs/ARCHITECTURE.md.

Settings and tuning

Press T in game. Every knob applies live — the game keeps running behind the panel, so you watch the change land instead of restarting to see it. Values persist to localStorage.

Settings panel

Feel — volume, screen shake, damage numbers. Screen shake goes down to zero on purpose: motion like that is a real accessibility problem and turning it off should not mean editing source.

Difficulty and Progression — the balance constants, exposed rather than buried. Handy for answering "what if the boss arrived at 1:30" without a rebuild, and for making the game easier or harder than the default without touching code. Reset to defaults puts everything back.

The panel is plain HTML on top of the canvas — <input type="range">, a checkbox, real <label> elements. That buys keyboard navigation, focus rings, screen-reader labels and touch support for free, none of which a canvas-drawn menu would have without a lot of code.

Tuning knobs in source

The same values, if you would rather set the defaults directly:

Knob What it does
spawnEvery(t) / batchSize(t) how fast enemies arrive
hpScale(t) how tough they get
xpForLevel(lvl) how often you level up (kept deliberately flat)
ETYPES per-enemy stats
WEAPONS / PASSIVES the upgrade tables — add an entry, it shows up on cards automatically
BOSS_T, MAX_ENEMIES, IFRAMES boss timing, population cap, hit forgiveness
EVO_WEAPON_LVL / EVO_PASSIVE_LVL how much commitment an evolution costs
HEART_CHANCE how often a kill drops the only healing in the game
VOLUME, SHAKE_SCALE, SHOW_DMG the player-facing settings

Anything in the TUNABLES table is reachable from the panel; adding an entry there is all it takes to get a labelled, persisted, tested control.

Damage per hit has to keep pace with hpScale or the whole thing stalemates at the population cap with nothing dying. That was the single biggest balance bug found by the sim.

Deliberate omissions

Marked in-source with ponytail: comments.

  • Brute-force collision — O(bullets x enemies). Measured 0.01ms/update at 340 enemies. A grid hash is the upgrade path if that ever stops being true.
  • No object pooling — GC handles a few hundred short-lived objects fine.
  • No sprites — shapes and particles. Zero asset pipeline.
  • No music — sound effects only, ~20 lines of WebAudio.
  • Population cap instead of a spawn budget — a hard ceiling is one line and does the same job.
  • No DPR scaling — crisp enough, and it halves fill cost on retina displays.

Docs

  • docs/ARCHITECTURE.md — how it fits together, and how to add a weapon, an enemy or a passive
  • docs/TESTING.md — the four test layers, what each one can and cannot see, and the three balance bugs the sim caught

License

MIT — see LICENSE.

Ship it

Build the upload bundle (it is gitignored, since it is just a copy of index.html):

Compress-Archive -Path index.html -DestinationPath swarm-itch.zip -Force

Drop the zip on an itch.io project page and tick "This file will be played in the browser". index.html must be at the zip root, which the command above guarantees.

About

A bullet heaven in one HTML file. No dependencies, no build step. Move; everything else fires itself.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages