worldmap-viewer
A 3D globe in the browser for worlds built with worldgen: fly from orbit down to ground level, inspect every value, measure, pin places, refine regions to ≈ 5 km detail, and export terrain squares for game engines. Runs locally (binds 127.0.0.1 only); three.js is vendored.
Setup
Clone next to worldgen (or set WORLDGEN=/path/to/worldgen, or put it inside this folder as worldgen/):
games/
worldgen/ the generator
worldmap-viewer/ this
myworld/ a world folder (config/, sketch/, out/ …)
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
.venv/bin/python serve.py --world ../myworld # open http://127.0.0.1:8765 — Ctrl-C stops it
Build the world first (worldgen/mapgen.py build). The first start after a build prepares caches (a minute or more
at res 5); later starts are quick. Tiles render on demand and are cached in the world's out/.
Commands
| Command | What |
|---|---|
serve.py [--world DIR …] [--res N] [--port 8765] [--lore DIR] [--workers N] [--public [--allow-host NAME] [--bind ADDR]] [--tile-cache-gb GB [--tile-keep-z Z]] [--max-renders N] |
the viewer. --lore: a folder of <id>.md notes pins may link to. Several --world: one server, each world at /w/<id>/ (id from [render] name), a world menu switches; --res then applies to all. --workers N: render processes (default CPUs − 2, max 6). --public: read-only map for anyone — no writes; pins, regions, exports and lore ids are not served, the page hides those tabs; tiles are sent immutable (their URL carries the version). --allow-host NAME: also answer requests for that public name (a reverse proxy or tunnel on the same machine passes it on); --bind ADDR (default 127.0.0.1; another address, e.g. 0.0.0.0 in a container behind a proxy, needs --public). --tile-cache-gb GB: cap on saved tiles (all worlds); past it the least recently used go (down to 90 %); --tile-keep-z Z keeps relief and mesh tiles to zoom Z (a shipped prerender) out of it. --max-renders N: at most N tiles rendering or waiting (all worlds), more get 503 + Retry-After and the page retries; --public defaults it to 4 × workers. Every option can also come from the environment (a container): WORLDMAP_<OPTION>, e.g. WORLDMAP_TILE_CACHE_GB=140, WORLDMAP_PUBLIC=1; repeatable ones as a list (WORLDMAP_ALLOW_HOST=a.org,b.org); a command-line flag wins |
mapview.py [--world DIR] refine [--final] |
refine all regions in places/regions.json (usually done from the viewer) |
mapview.py export --lat .. --lon .. [--size-km 40] [--res-m 10] [--pixels N] [--name ..] [--era ..] |
heightmap + masks for a game engine |
mapview.py seafloor-preview --region <id> |
image of a refined region's sea floor |
Using it
- Globe / Equal Earth / Plate carrée views; layer menu (relief, elevation, temperature, rainfall, biomes, ground, ores, plates, sea floor, gravity, O₂, …) with a second layer blended over it.
- Era menu (if eras.toml has eras). Overlays: coasts, lakes, rivers, graticule, regions, pins.
- Zoom to ground level: 3D terrain (exaggeration slider), right-drag / Shift+drag to tilt and turn, N resets, 👁 ground view. ? lists all controls.
- Inspector: click any point for every value there. Measure: distances, areas, elevation profile.
- Pins: named markers (stored in
places/pins.json). Search box: pins,40N 10W, H3 cell ids. - ☀ Sky: sun position for any day and time; tropics and polar circles.
- Regions: draw an outline, Save and refine → real ≈ 5 km erosion, rivers, lakes and biomes in that area
(background job; seconds to minutes). Stored in
places/regions.json. Very large regions take much RAM. - Export: cut a terrain square (heightmap .f32/.png/.r16, water, biome masks, rivers, pins) into
exports/for Unity / Godot / Unreal. Each export has aREADME.txtwith import settings. - The link button copies a URL with the exact view.
The first start after a build prepares caches (a minute or more at res 5); later starts are quick. Tiles render on
demand and are cached in out/.
Public server (container, read-only, copied worlds): deploy/README.md (deploy.py bundle / dedupe, deploy/Containerfile).
Low memory: serve.py --low-memory / mapview.py --low-memory … (or WORLDGEN_LOW_MEMORY=1) builds those caches with
each field written to disk as soon as it is ready (a .spill-* folder, removed afterwards) instead of all fields in
RAM: a slower first start, a lower peak (≈ −40 % for the refined-area cache), the same arrays.
Optional in-world units: [units] in world.toml with span_m, league_km, moment_s adds spans/leagues next to
metres/kilometres, and the sky panel counts moments instead of hours:minutes.
Files
serve.py server viewer/ browser app (js/, tests/, tools/smoke.sh)
tiles.py servecache.py refine.py rivers.py seafloor.py export.py mapview.py
worldgen_path.py finds worldgen
tests/ python -m unittest discover -s tests -t . · cd viewer && node --test tests/*.test.mjs
Troubleshooting
worldgen not found→ clone it next to this folder or setWORLDGEN.- Server says no build → build the world first (or pass
--resto pick one). - Port in use →
--port 8766.
Licence and credits
MIT, see LICENSE. Third-party notices: licenses.md. Credits: CREDITS.md.
From the author
These projects are things that have been tumbling about in my head for a long time, and I now feel like trying to do something with. The bulk of my focus here is on some of the games that I love, but every time I boot them up, I just fiddle about in the menu and set up mods and fixes for a couple hours, with my drive to play them fizzling out. This is my hope to solve that, and while I am a professional software engineer this would not have happened were it not for the rise of (relatively) cheap AI that could do the bulk of the work with me. If you don't approve, that's fine. I did these things for me, sharing them is something I do in the hopes to help others in similar situations, that just want to play the games they love. Thank you to all that made these playable in the first place, with some luck this finds you, and can bring some joy.
Written with AI assistance (Claude, by Anthropic), used as an engineering tool.
