A top-down arena tank game. Clear the arena; one shot kills anything, including you.
Three.js renderer over a pure, deterministic 2D simulation — swept ray-vs-AABB shell physics with in-tick reflection, proximity mines, and a data-driven enemy roster.
The canonical roster currently defines 6 enemy types. Its gameplay data lives in
tank-defs.json and
ai-profiles.json.
| Enemy | First mission | Movement | AI profile | Mines | Shells |
|---|---|---|---|---|---|
| Brown | 1 | Stationary | Static basic | No | Standard shell; slow fire; max 5; 1 bounce |
| Grey | 2 | Medium | Defensive basic | Yes (2) | Standard shell; medium fire; max 5; 1 bounce |
| Olive | 3 | Slow | Defensive rocket | No | Rocket; slow fire; max 1; no bounces |
| Teal | 5 | Slow | Mobile mine layer | Yes (2) | Ricochet rocket; fast fire; max 5; 2 bounces |
| Yellow | 8 | Medium | Mobile mine layer | Yes (4) | Standard shell; medium fire; max 1; 1 bounce |
| Green | 12 | Stationary | Ricochet sniper | No | Ricochet rocket; slow fire; max 5; 2 bounces |
| Move | WASD / arrow keys |
| Aim | Mouse |
| Fire | Left click |
| Drop mine | Space / right click |
| Mute | M |
Experimental gamepad input and 2–4-player couch co-op are available behind development flags; see the generated dev-flag reference.
Requires Node ^22.13.0 || ^24.0.0.
npm ci # install from package-lock.json
npm run dev # Vite development server
npm run verify:quick # typecheck + Vitest suite (`npm test` is an alias)
npm run verify:build # production bundle + subpath-portability check
npm run capture -- --list # list reproducible screenshot/clip recipes
npm run preview # serve the built bundleThe atomic npm run typecheck, npm run test:unit, and npm run build commands each
perform only the named operation. See the verification command reference
for full, visual, and subsystem-specific checks.
Reproducible local review media is documented in the capture recipe reference.
Design direction, implementation plans, deferred spikes, and research are listed in the documentation index, which is generated from each document's own metadata and separates current direction from the implementation record.
src/sim/ is a pure, deterministic core: given a seed and an input stream it
produces the same world, tick for tick. It imports nothing from Three.js, the
DOM, or Howler — src/sim/purity.test.ts enforces that automatically and fails
naming the offending file and token.
Everything else is a one-way projection of sim state:
src/render/interpolates between the previous and current world at the display's refresh rate, independent of the fixed 60 Hz sim.src/audio/mapsSimEvents to sound.src/game/owns the fixed-timestep loop, the state machine, and the HUD.
The build sets base: './', so dist/ is portable to any static host and any
subpath without reconfiguration.
None are committed. public/audio/ holds only a .gitkeep, so the manifest
declares nothing and the engine synthesises every sound in Web Audio rather than
requesting files that are not there — a deliberate choice, not a degraded mode,
so development is never blocked on assets. (It used to declare all ten anyway and
let each 404: 10 requests and 93,790 uncached bytes per load, measured against the
deployed site.) See CREDITS.md; its attribution tables are
intentionally empty rather than fabricated.
Source-available, not open source. The two are different, and the difference is the point: you can read this code, but it does not carry an open-source grant.
Three things govern this repository, and every file is under exactly one of them:
- Code, tests and repository tooling — PolyForm Shield 1.0.0, the official text, unmodified.
- First-party content — artwork, audio, levels and maps, narrative and copy, media, logos and branding — All Rights Reserved. See CONTENT-LICENSE.md, which classifies paths so a reader can tell which of the three applies to any file.
- Dependencies — their own licenses, recorded in THIRD-PARTY-NOTICES.md.
Shield permits reading and private experimentation, but not using the software to
provide a competing product — and that holds whether a competing release is paid or
free. LICENSE controls; this paragraph is a summary, not the terms.
The owner may grant exceptions, commercial licenses or modding permissions separately, and separately granted written permission overrides the default.
Outside code and asset contributions are not being accepted yet — see CONTRIBUTING.md for why, and for what is welcome instead.
This section records the project's licensing boundary. It is not legal advice.