Pick an area on the map. The browser fetches that area's real elevation, the weather radar currently over it, and the soil underneath, then runs a water flow simulation on the result. No API keys, no server, no data files.
Terrain is shaded by height and lit from the north west. Blue dots are live water, the fainter lines behind them are where water has recently run, which is how the drainage network draws itself. Rain comes from live radar, and how much of it soaks away depends on the soil.
Rust does the flow simulation and nothing else. It takes a plain grid of heights and an infiltration grid, and knows nothing about maps, projections or file formats. JS does all the geodata: Web Mercator tile math, fetching, decoding, stitching.
Everything decodes with native browser APIs. ImageDecoder for PNG,
DecompressionStream for deflate. Tile payloads are data, not pictures, so they
never go through a canvas: colour management can shift a channel, and one step
in red is a 256 m elevation error.
| Layer | Source |
|---|---|
| Elevation | AWS Terrain Tiles (terrarium PNG) |
| Rain | RainViewer radar + Open-Meteo ERA5 climate baseline |
| Soil | ISRIC SoilGrids sand/silt/clay |
| Place search | Photon |
| Basemap | CARTO / OpenStreetMap |
src/lib.rs the simulation, pure Rust
www/tiles.mjs tile math and the shared fetch/decode/stitch
www/elevation.mjs terrain tiles to metres
www/rain.mjs radar to mm/h per cell
www/climate.mjs long-term mean rain from Open-Meteo ERA5
www/rainfield.mjs composed rain field (radar + climate + gain + brush)
www/soil.mjs texture to infiltration capacity
www/places.mjs place name to a map view
www/index.js map, render loop, rain spawner, UI
Needs Rust, wasm-pack and Node 18+.
wasm-pack build # writes pkg/, which www/ depends on
cd www
npm install
npm start # http://localhost:8080wasm-pack build has to run first. www/package.json depends on file:../pkg,
so npm install fails if pkg/ is not there yet. Re-run it after any change to
src/.
cargo test # flow algorithm
cd www && npm test # tile math, elevation, rain, soil, searchThe JS suites are plain node file.test.mjs with node:assert. No framework,
no runner, no config.
Live at https://water.huseyincapan.dev. The image builds the wasm and the bundle
itself, then throws both toolchains away and serves the static output with Caddy,
which gets the application/wasm MIME type right without being told.
cp deploy/deploy.env.example deploy/deploy.env # once, fill in for your box
./scripts/deploy.shThe script ships git archive HEAD, so commit first. It refuses a dirty tree.
Nothing about the target machine lives in this repo: the host layout goes in
deploy/deploy.env (gitignored) and the ssh address in ~/.ssh/config.
- Basic water flow dynamics
- Render real world height data
- Pick the area to simulate from a map
- Live rainfall from weather radar
- Soil absorption from a soil survey
- Land cover, which matters more than soil in built up areas
- Soil saturating as a storm goes on, so late rain runs off
MIT or Apache-2.0, at your option.
Started from wasm-pack-template.
