Atlas Arcade
Offline geography games built on one local world dataset



01 The story
Geography games that run fully offline: a 3D globe guesser, a distance-and-direction country hunt, border chains, stat duels, capitals and flags. They all read one country dataset I built from REST Countries, Natural Earth, the World Bank and Wikidata. React on the web, then Flutter for phones.
Atlas Arcade started from a data idea, not a game. I wanted one clean, local 'world engine' of countries, borders, shapes and statistics, and then to see how many geography games it could power. Nothing in gameplay calls a public API. Every mode reads the same normalized JSON and GeoJSON files.
A TypeScript build script fetches REST Countries, Natural Earth 1:50m boundaries, nine World Bank indicators and Wikidata facts like anthems and highest points. It caches the raw downloads, matches records across sources by ISO3 code with fallbacks and a manual alias map, validates the output with Zod, and writes a report of anything that didn't match. The result covers 250 countries and territories, 239 with boundaries and 245 with statistics.
The globe uses no map tiles. Natural Earth polygons are painted onto a 2048x1024 canvas texture wrapped around a three.js sphere. A click raycasts to UV, converts that to latitude and longitude, and picks the nearest country by haversine distance. The React web app wraps it in GlobeGuessr, alongside Country Crunch, Border Chain, Higher/Lower, Capital Match, Map Tap, Stat Duel, Flag Frenzy, a Facts browser and a date-seeded Daily.
In May 2026 I ported it to Flutter as a phone app with 14 modes, including new ones like Density Duel, Compass Quest, Border Brawl, Tongue Twist, Famous Foods, Landmarks and Continent Match. Both versions were built between late April and mid-May 2026 and have been dormant since. Neither was deployed or published.
02 What it does
- 01
GlobeGuessr: spin a 3D globe and click the hidden country, guided by distance clues
- 02
Country Crunch: find the secret country from distance, compass direction, region, population and area clues
- 03
Border Chain: build the longest chain of countries linked by land borders, with no repeats
- 04
Higher/Lower and Stat Duel on population, GDP, life expectancy and other World Bank figures
- 05
Capital Match with region-weighted distractors, Flag Frenzy, and Map Tap on real country shapes
- 06
A Daily puzzle seeded by date and mode, so everyone gets the same round with no server
- 07
Game Lab with 19 pitched modes, and a Data Explorer that shows the dataset's quality warnings
- 08
Flutter phone version with 14 modes, including Density Duel, Compass Quest, Tongue Twist, Famous Foods and Landmarks
03 How it's built
No backend. Public data is downloaded once at build time and shipped as static files, so every game works offline. The Flutter app bundles a subset of the same files plus a curated list of foods and landmarks.
All 5 connections, as text
- Public country data (4 sources) → TS data build: cache, match, validate: fetch once, cache
- TS data build: cache, match, validate → Local JSON + GeoJSON world dataset: normalize + report
- Local JSON + GeoJSON world dataset → React web arcade: static files
- React web arcade → three.js globe: lazy-loaded
- Local JSON + GeoJSON world dataset → Flutter phone app: bundled assets
04 Engineering notes
Offline-first data build: one fetch of REST Countries, Natural Earth, World Bank and Wikidata, cached raw and normalized into seven local JSON/GeoJSON files. Gameplay never touches the network
Cross-source matching: ISO3 first, then Natural Earth fallback properties and a manual alias map. A generated report lists the 13 territories without a 1:50m boundary and 3 Natural Earth regions (like Somaliland) with no country record
Tile-free globe: polygons painted onto a 2048x1024 equirectangular canvas texture on a three.js sphere. A click raycasts to UV, converts to lat/long and picks the nearest country by haversine. The globe page lazy-loads three.js
Zod schemas validate the generated data, and Vitest covers the geo math (haversine, bearings, compass points), search and each game engine
The Flutter port re-implements the game logic in Dart with its own unit tests and a token-based light and dark theme
05 By the numbers
- 250
- Countries and territories
- 239
- With map boundaries
- 245
- With statistics
- 9
- World Bank indicators
- 10 modes + Game Lab
- Web game modes
- 14
- Flutter game modes
- 19
- Pitched modes in Game Lab
06 Stack
App
- React 18
- TypeScript
- Vite
- Tailwind CSS
- three.js
- Leaflet (dependency)
- lucide-react
- Flutter / Dart
Data
- REST Countries
- Natural Earth 1:50m Admin 0 boundaries
- World Bank indicators API
- Wikidata
- Zod
Tooling
- tsx data scripts
- Vitest
- flutter_test
- flutter_lints
Lines of code (approx.)
- Dart5,346
- TypeScript4,210
- CSS59
≈ 9,615 lines
Repositories
- geoprivate
- atlas_arcadeprivate
Both repos (geo and atlas_arcade) were backed up from the SSD on 2026-11-09 with one commit each, so git history is lost. Dates come from file timestamps: the web app's scaffold, data scripts and first data build are from 2026-04-25; Border Chain, Higher/Lower and Capital Match were added on 2026-05-18; the Flutter project was created on 2026-05-18 and last changed on 2026-05-19. firstCommit and lastCommit here are those work dates, not git dates.