Overview
The whole game is a single-page three.js application. There is no engine layer, no ECS framework, no physics library — the renderer, the simulation, the AI, the audio synth and every texture on screen are project code. That choice is what makes the rest of this page possible: every system below is inspectable in the repository, and almost everything is generated at runtime from deterministic seeds rather than shipped as assets.
- One render loop, one context. A single
WebGLRenderer(antialias off, stencil off, high-performance power preference) drives garage, battle, kill-cam and the scene studio. All presentation goes through one EffectComposer chain — nothing ever callsrenderer.renderbeside it in live play. - Runtime generation. Tank textures are painted on 2D canvases, terrain is sampled from a 9-octave simplex stack, vegetation and buildings are placed by seeded RNG streams, cloud decks are fbm bakes. The deployed page transfers ~3.6 MB.
- Determinism as a contract. Every generator draws from
mulberry32streams with fixed seeds. The same battlefield, the same paint job and the same wreck char render bit-identically across sessions — which is what lets the project pin screenshot and geometry contracts in CI-style gates.
Engine & rendering
The renderer is configured once at boot: ACES filmic tone mapping (exposure 1.16, tuned by pixel-measured playfield luma against a reference band), sRGB output through an OutputPass, PCF shadow maps, and a canvas pixel-ratio cap of 2. The canvas backing store is 1:1 with physical pixels on dpr ≤ 2 displays; the composer's internal resolution is governed separately (see the quality governor), so the final antialiasing pass always rasterizes at native resolution even when the 3D frame renders smaller.
Post chain & antialiasing
Context AA is deliberately off. The scene renders into a quality-tier MSAA target (up to 4×), resolves once, then runs a single-sampled post chain: half-resolution GTAO with Poisson denoise, threshold bloom with a pre-tonemap emissive shoulder (so fireball cores don't clip flat), a grade S-curve pivoted at 0.33, aerial/haze, and a final display-space SMAA/FXAA pass that steps in native canvas pixels. Passes are merged where possible — each extra pass is a full-screen render.
Lighting & shadows
One sun. A four-cascade CSM rig owns the only shadow-casting directionals; a hemisphere light and an anti-sun sky fill (shadowless) complete the set, plus exactly two pooled PointLights shared by every muzzle flash and explosion — pooling keeps the scene's light count constant, so three.js never has to relink shader programs mid-battle over a changed light count.
- Near cascades re-render every frame; the two far cascades (hundreds of meters out) re-render round-robin, one per frame — camera motion per frame is a fraction of a far-cascade texel, so it's visually free.
- The governor can throttle the whole rig to half or third refresh under measured load (
setShadowThrottle), with staggered scheduling and a forced full redraw on every transition so a stale map is never presented. - Instance-level shadow culling compacts instanced meshes against each cascade's frustum before the depth pass — shadow fill rate is ~17% of total draw calls and earns its own optimization lever.
The sky is a dome with two independent cloud decks — sun-aligned cumulus and cross-wind cirrus — whose textures are seeded fbm canvas bakes, baked lazily (one deck per frame on the loading path) and swapped in over 4×4 placeholders.
How tanks are made
Every vehicle in the game exists twice: as a procedural build — geometry constructed from measured real-world dimensions, painted by canvas programs — and, for much of the modern fleet, as a community-sourced GLB that streams in over the procedural stand-in. The procedural build is always the model of record: it spawns instantly, it works on every device tier, and it is what the license-stripped public build falls back to.
Procedural builds
Each tank family lives in a profile module (Abrams, Leopard, Merkava, Soviet heavies, WWII marks…). A profile declares real dimensions — hull length, track gauge, turret ring, gun caliber — and composes the vehicle from a shared construction kit: multi-segment hull plates with authored glacis angles, turret prisms inscribed in certified envelope curves, mantlets, skirts, fenders, and a dense fitting layer (tow hooks, periscopes, stowage, ERA bricks, antennas) that gives silhouettes their read at distance.
- Running gear is instanced. Road wheels, sprockets, idlers and every track link render as
InstancedMesh— a tank's few hundred link pads cost one draw. Per-wheel terrain conform and track scroll write instance matrices; a distance gate drops conform to staggered thirds beyond 160 m. - Geometry gates. Sourced reference models double as measurement oracles: automated probes compare the procedural build's silhouette, station positions and plate proportions against the oracle from 14 views, and a family only "graduates" when it passes a dual gate (machine metrology + independent shaded-parity critic). The registry, ledgers and frozen hashes live in
docs/geometry-gate/.
Texture painters
No tank texture ships with the game. Each spec owns one shared canvas set — albedo (2048² for the hero tier, 1024² for AI roster fills), a height-derived normal map, roughness with per-patch paint response — painted by deterministic canvas programs: base scheme, panel lines and weld rows (welded-composite hulls skip rivets), chips, streaks, dust, and one of 48 camouflage patterns from three-tone NATO summer to dazzle, flecktarn, digital, winter whitewash and autumn schemes.
A painted 2048² albedo is ~200 ms of main-thread work but zero network bytes and infinitely restylable: the camo picker, the per-battlefield AUTO paint and the biome repaints all redraw the same shared canvas in place — every live instance of that tank updates through one texture.needsUpdate. All painters run as generator programs so loading paths bake them in stage-sized slices instead of one freeze (see Performance).
The sourced-model program
The modern fleet's showcase models come from community artists (credited per-model in ATTRIBUTION.md, license-ledgered per file; non-commercial models are quarantined out of public builds automatically). A sourced model is never dropped raw into the scene — it goes through a staged pipeline that runs in battle-safe idle slots:
Camo on a GLB is composited in texture space: the pattern tile (mirror-stamped 3×3 so blob constellations never repeat across a hull) is blended with a luminance-normalized grayscale of the artist's albedo, preserving their weathering and AO while unifying the paint job. The procedural stand-in covers the stage until commit, so no frame ever shows a half-textured model.
Wrecks
A kill swaps the vehicle to charred materials derived from its own camo canvas: multiply-and-desaturate char stack, sooty blotches, rising heat streaks, rust bloom and bare-metal scrapes, plus a separate ember emissive map that smolders in seams. Destruction can pop the turret with the real physics arc; the kill-cam replays the killing shell with an x-ray module breakdown.
World generation
Battlefields are grown, not loaded. A heightfield samples a 9-octave simplex stack — domain-warped ridges plus detail octaves — then layers gameplay-aware features: a road network with smoothed corridor elevation (precomputed on a 257² field grid), a flattened village rectangle, marsh dips, lakes with auto-leveled shorelines, and per-biome macro forms (dune ridges, flat-top mesas with talus aprons) that fade around spawn pads and drive corridors so play always flows.
- Terrain renders as an 8×8 grid of chunks, three LOD meshes each with hysteresis switching, under one splat material (canvas-baked masks; slope-driven rock only on real landforms).
- Vegetation is fully instanced: tree clusters, lone pairs and map-rim stands with per-instance color jitter and vertex-shader wind; billboard impostors past the near partition (promoted in the sniper scope's aim corridor); a camera-centred dense grass carpet that re-parks its instances as you drive, plus midfield tuft chunks.
- Props & destructibles — cottages and rowhouses with plaster/roof/wood canvas prints, boundary walls, fences, trucks, lampposts, crops, sandbags — live in per-kind instanced pools. Breaking one is two matrix writes: zero-scale the intact slot, activate the broken slot. A rematch restores the whole map in O(1) per prop.
- Hot-path height queries (LOS ray marches, AI probes, wheel conform in battle) read a lazily-baked 1 m bilinear grid — ≤1 cm from the analytic surface, ~50× cheaper than the noise stack. Anything that seats visible geometry deterministically (spawns, captures) keeps the exact analytic function.
The whole build runs through generator cores that yield between slices — terrain per chunk-row, vegetation per placement pass, props per family — so the loading bar reports real progress and the build never freezes a frame. The synchronous twin drains the same generators for screenshot-contract map switches: both paths produce bit-identical worlds, verified by a fingerprint parity gate.
Simulation
Gameplay runs on a fixed-step simulation decoupled from the render loop, with an accumulator that drains cleanly across hitches. Nothing in presentation writes game state.
- Movement solves hull pose from four corner probes plus per-gear-line terrain samples, with ground-type speed effects (soft marsh bog-down, hard roads), engine/brake model, handbrake, and gun handling: the turret chases
input.aimPointunder hull-arc limits while dispersion blooms with movement and traverse, exactly WoT-style. - Ballistics & armor — five shell classes (AP, APCR, APFSDS, HEAT, HE) with penetration falloff over distance, impact-angle normalization, ricochet thresholds, spaced-armor and ERA interactions, and hull/turret/module hitboxes. HE resolves splash through terrain occlusion.
- Spotting is a real concealment simulation: view range vs camouflage factor, foliage transparency along the sight line (bushes attenuate rather than block), firing bloom that scales with caliber, proximity detection through cover, and per-team intel — bots acquire targets through the same spotting state the player gets, never by cheating.
- AI runs doctrine tiers (easy/normal/hard): cover evaluation and hull-down seeking against the height grid, obstacle avoidance, focus-fire discipline, low-HP retreat toward living teammates, and per-battle seeded personalities so replays are reproducible.
The movement, combat, spotting and equipment models are each pinned by headless self-test suites (28 / 233 / 99 / 166 assertions) that run on every landing.
Performance system
The performance architecture has one rule: anything that can hitch a live frame must either happen behind a screen the game owns, or be sliced until it can't. Everything heavy — painting, parsing, compiling, linking, uploading — is attributed by probes (frame-gap recorders, long-task observers, CPU profiles, per-slice pipeline telemetry) and then moved or chunked.
Loading & the warm pipeline
- Chunked everything. World subsystems, tank family bakes (per painter stage), wreck char bakes, camo repaints, cloud decks, GLB merges — all generator-sliced with a painted frame between slices. The loading bar creeps through subsystems instead of jumping between freezes.
- The warm pipeline runs behind the pre-battle screen: a silent volley of every effect family at a far map corner (sprite atlases bake, then one live-fx frame binds their pipelines), destroyed-state dances for every fielded family (wreck canvases + burn-hook shader variants compile), a whole-scene compile, then hidden-variant compiles per tank.
- Shader links never block a reveal. Program compilation is submitted mesh-batched on a 12 ms budget, then the driver's parallel linker gets breathing frames (a bounded
KHR_parallel_shader_compilepoll) before anything renders. Shadow-depth variants — which only ever build at a real render — are resolved by warm renders of the world in subsets (terrain → +vegetation → +props) at a quarter viewport: same pipelines, 16× less fragment work. - A spawn-view warm frame renders one hidden frame from the player's chase pose before the countdown, so the first battle frame binds nothing new — measured zero program links at battle-open.
- The 5-second countdown (sim frozen, world visible) doubles as an absorption window: a chunked re-warm covers any straggler models that outran the loading screen's bounded drain.
The quality governor
Quality follows measured performance, not device class. A per-frame governor tracks the render delta EMA, a spike-miss ratio, and the display's true cadence (p10 of recent deltas), makes a decision every 2.5 s of visible frames, and walks a relief ladder in both directions:
| Lever | What it does | Engages |
|---|---|---|
| Dynamic resolution | Composer scale steps 1.0 → floor (0.75 preset / 0.8 on dpr-1) in 0.09 steps; final AA stays native | First — every pixel ratio |
| Trim 1 — shadow rate | All cascades to half refresh, staggered | Scale at floor, 2 overloaded windows |
| Trim 2 — ambient occlusion | GTAO off (the most expensive single pass) | Next 2 overloaded windows |
| Trim 3 — shadow rate | Cascades to third refresh | Next 2 overloaded windows |
| Adaptive tier | Whole preset steps down, persisted across sessions | Last resort, ladder exhausted ~10 s |
- Framerate-decline detection. The governor earns an fps baseline during clean windows (rises fast, sags reluctantly) and treats a window that loses ≥20% of it — below a 72 fps smooth-enough ceiling — as overloaded, even when frames sit under the absolute budget. This catches the two blind spots of budget-only governors: a uniform 45–52 fps crawl with zero spikes, and a 120 Hz display sagging to 70.
- Recovery walks the same ladder back — trims release first (they're visually loudest), resolution re-earns to 1.0 — with exponential flap backoff so an intermittently-loaded machine never oscillates.
- Capture contexts reset everything: screenshot and metrology contracts always render untrimmed at full scale, and cascade redraws are forced, so the certified pixel contracts never see the governor.
- A boot-time device diagnostic probes basic/lit/lit+shadow rendering and self-rescues (e.g. shadows-off with an exposure band reclaim) on GL stacks that fail — with a collapsing on-screen report. GPU-string heuristics only pick the starting tier; measurement takes it from there.
Mobile
Touch devices resolve the mobile tier (UA + touch points + GPU limits) and get a Blitz-style battle HUD: left drive joystick with a texture-triangle D-pad, swipe-to-aim over the right half, fire/scope/brake cluster, pinch-to-zoom into sniper, consumables re-parked as a thumb column, and a top-right ammo tray with ≥44 px targets. Portrait works with a landscape recommendation; safe-area insets are respected everywhere; browser pinch-zoom is defeated in depth. The mobile tier halves texture bakes and shadow budgets, and the same governor runs on top — verified end-to-end on iOS Safari (real WebKit, dpr 3) in the simulator, where the shadow-sampling rescue and tier detection were exercised for real.
Audio
The audio engine is WebAudio with two complete backends: a baked sample set (dense layered PCM synthesized offline, fetched and decoded after the first user gesture) and a live synthesis fallback that can produce every combat sound from oscillators and shaped noise if any sample fails — gunfire by caliber class, penetration clang, ricochet whine, HE crumps, engines, tracks, ambience and UI. Distance drives low-pass filtering and whump emphasis; repeats get playback-rate jitter so nothing sounds cloned.
UI & game modes
- Garage — pedestal showroom with a damped drag-orbit camera (fixed canonical framing so switching tanks never slides the eye), camo picker with live repaints, battlefield picker with biome-AUTO paint preview, era-filtered carousel, tech tree, equipment loadouts and per-tank stats.
- Battle — WoT-style HUD: shell tray with reload ring, minimap with real top-down capture underlay, damage panel with module states, kill feed, hit indicators, spotting eye, and an in-world 5-second countdown before control releases.
- Kill-cam & end screen — slow-motion tracer replay of the killing shell with x-ray module breakdown, then a cinematic results screen (credits/XP economy, tallies, kill list, team panels) that adapts down to phone landscape.
- Scene studio (press F8 in the garage) — a full staging tool: place any vehicles on any battlefield, pose turrets, trigger effects (fires, smoke, tracers, wrecks), drive cinematic cameras and export stills — it's how every marketing shot on the home page was made.
QA & determinism
The project treats rendering like an API with contracts:
- Self-tests pin the simulation math (526 assertions across movement, combat, spotting, equipment) and run headless in Node.
- Screenshot contracts stage deterministic battles and hash-compare renders; the geometry-gate program freezes graduated vehicles against vertex oracles and re-certifies on any change.
- Probe suites drive the real game in headless Chrome: a journey probe (every first-use interaction), a play-session recorder (minutes of driving/fighting with frame-gap, long-task, shader-link and readback logging), a governor end-to-end (deterministic overload via frame burners), boot/battle-load stage telemetry, and world-build parity fingerprints.
- Everything seeded. Worlds, paint, AI personalities, wreck chars and cloud decks derive from fixed
mulberry32streams — the foundation that makes every contract above possible.
Deeper internal docs live in the repository: docs/ARCHITECTURE.md (system contracts), docs/ATTRIBUTION.md (every sourced model and license), and docs/geometry-gate/ (the vehicle metrology program's registry and ledgers).