A low-poly browser game with one idea: the valve is stuck open. You are a small firefighter attached to a hose that never stops spraying. Aiming the water at the fire also aims the recoil at you β the hose is both your tool and your transport, and it has opinions.
Ragdoll physics, a wildfire that actually spreads with the wind, and a fire truck that gets dragged along behind you. Built with Three.js and Vite; no game engine, no asset files β terrain, trees, audio and UI are all procedural or hand-rolled.
npm install
npm run dev # http://localhost:5173npm run build produces a static bundle in dist/. Pushes to main deploy to
GitHub Pages automatically via .github/workflows/deploy.yml.
| Key | |
|---|---|
Arrows / WASD |
swing the nozzle |
Space |
full blast β the recoil is the ride. Aim away from where you want to go |
Shift |
pinch the hose β no water, boots dig in |
Esc |
pause |
R |
restart the shift |
The camera follows the nozzle, so wherever you're aiming is what you see. Since the reaction force points the other way, travelling anywhere means turning your back on it and blasting.
Hold the ridge for three minutes. You lose if more than 45% of the forest burns down. Lightning keeps starting new fires, wind decides which way they spread, fire climbs uphill faster than it runs downhill, and embers ride the wind to start spot fires ahead of the front.
There is no score, no meter, no resource. The forest is the score: every tree you rescue stays a brighter green (on the ground and on the map), every tree you lose stays a scorch mark. At the end of the shift the valley shows you exactly how you did.
Water soaks whatever it touches. Spraying a healthy tree makes it much harder to ignite, so hosing down the trees ahead of an advancing fire works as a firebreak. The faint arc and ring show where the stream will land. Stand too close to a blaze and the heat makes you yelp-hop away on its own.
| File | |
|---|---|
src/main.js |
scene, lights, camera rig, game loop, HUD, screens |
src/player.js |
jet physics, verlet ragdoll, hose rope, the dragged truck |
src/forest.js |
tree instancing, fire-spread simulation, water absorption |
src/effects.js |
fire / smoke / ember / water / steam behaviour |
src/particles.js |
pooled GPU point system with per-particle size and colour |
src/terrain.js |
heightfield, vertex colouring, scorch marks |
src/aimguide.js |
traces the water's ballistic path, draws the landing ring |
src/decor.js |
set dressing: boulders, grass, flowers, clouds, a bird flock |
src/audio.js |
procedural hiss, fire bed, chimes, impacts β no audio files |
src/noise.js |
value noise + fbm |
The player integrator substeps at ~100 Hz regardless of frame rate. The hose is a verlet rope that snags on tree trunks; tension is applied along the rope's actual path, so a hose wrapped around a tree becomes a swing anchor, and the truck is dragged along the path rather than through the forest. The nozzle wanders under thrust, steep slopes shed you downhill unless you pinch the hose, and the truck collides with trunks.
Trees are three InstancedMesh draws regardless of count; every particle
system is a single Points draw with swap-removal so the live range stays
packed; fire spread uses a precomputed neighbour graph over a spatial hash.
Fonts (Lilita One + Nunito) are bundled locally via Fontsource β the built site
makes no third-party requests.
window.__game exposes the player, forest, audio and game state for poking at
things from the console.
