A fantasy-style retro world map rendered with OpenLayers. Ships as a fully static site: no backend, no API calls at runtime — all the geographic data (terrain, cities, volcanoes, seamounts, megalithic monuments, several thousand historical events) is pre-baked at build time and bundled with the page.
build-data.mjs Downloads upstream sources and emits data/*
data/ Generated by build-data.mjs, committed to the repo
static/ Cursors, fonts, favicon
index.html
main.js
cities-worker.js Worker for the 200k-city bbox scan / search palette
points-worker.js Generic worker — volcanoes / seamounts / megaliths / facts
npm install
npm run dev # http://localhost:9191data/background.pmtiles and data/cities.bin are committed, so cloning the
repo is enough to run the site. Regenerate them whenever you want to pick up
fresh upstream data — this needs tippecanoe
on PATH:
brew install tippecanoe # or apt-get install tippecanoe on Ubuntu
npm run build-data # ~30 MB download, cached in .data-cache/
git add data && git commit -m "refresh data"CI does not need tippecanoe — .github/workflows/deploy.yml consumes the
committed data/background.pmtiles directly.
build-data produces several files in data/:
background.pmtiles— NaturalEarth layers (land, lakes, rivers, 11 stepped bathymetry bands, glaciers, marine labels, parks, mountain-range polygons) baked into a PMTiles vector-tile archive by tippecanoe. Per-class MVT layers, per-zoom simplification, per-featuretippecanoe.minzoombaked in (e.g. low-rank rivers, regional river_detail, and the abyssal 10 km bathymetry are physically omitted from low-zoom tiles). The browser fetches tiles via HTTP byte-range requests from a single archive — no per-tile DNS, no runtime reprojection, no multi-megabyteJSON.parseon the main thread.cities.bin— every GeoNames city ≥500 inhabitants, packed binary, pre-sorted by a score that boosts capitals (+1B) and district capitals (+100M) over raw population. Coordinates are pre-projected to EPSG:3857 metres so neither the worker bbox scan nor the main-thread feature construction has to callfromLonLat. Layout:u32 count,f32[count] xs,f32[count] ys,u32[count]populations,u8[count*2]country codes, name+search offset/byte sections.volcanoes.bin,seamounts.bin,megaliths.bin,facts.bin— packed point datasets in a shared layout consumed bypoints-worker.js. Sources: Smithsonian Global Volcanism Program (~1500 Holocene volcanoes), and Wikidata SPARQL queries (seamounts ≥2 sitelinks under Q207326; dolmens, menhirs, stone circles, passage tombs, megalithic tombs, barrows; and historical events with both coordinates and apoint in time— battles, sieges, treaties, shipwrecks, expeditions, eruptions, disasters, …). Each feature carries a per-featureminZoomso the LOD curve is baked into the data rather than computed at render time.
Re-run npm run build-data whenever you want to refresh the upstream data;
the .data-cache/ directory makes repeat runs free.
The original backend used a geohash index to return the top-N highest-scored
cities inside a viewport. The static version pushes that work into a
Web Worker (cities-worker.js):
- Main thread fetches
cities.binas anArrayBufferandpostMessages it to the worker with the transferable list — zero-copy handoff, no parse, no main-thread jank. - Worker maps
Float32Array/Uint32Arrayviews over the buffer. - On every
moveend, main posts a bbox query; worker scans the cities in score order and returns the first 100 inside the viewport.
Because the array is globally sorted by score, the loop terminates within the first few hundred entries for any populated viewport, and is bounded by a ~2 ms full-array sweep in the worst case (sparse viewport, empty ocean). Stale query results are dropped on the main thread by tracking the latest query id.
npm run build # outputs to dist/Parcel builds with --public-url ./ so the bundle works under any subpath,
including the /<repo>/ path used by GitHub Pages.
.github/workflows/deploy.yml runs npm run build on every push to master
and publishes dist/ to GitHub Pages. It uses the data/ files committed in
the repo — CI does no data download.
To enable: in the repo's Settings → Pages, set "Build and deployment" source to GitHub Actions.
- NaturalEarth — public domain
- GeoNames cities500 — CC BY 4.0
- Smithsonian Global Volcanism Program — CC BY 4.0
- Wikidata — CC0 (SPARQL endpoint)