Atlas Arcade

Offline geography games built on one local world dataset

Status
PrototypeDormant since May 2026 and never deployed or published. The web build exists; the Flutter port runs locally only.
Platforms
Web, iOS, Android, macOS
Role
Solo: design, data pipeline, web app and Flutter port
Timeline
Apr 2026 – May 2026
Version
Web 0.1.0 · Flutter 1.0.0+1 (unreleased)

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

  1. 01

    GlobeGuessr: spin a 3D globe and click the hidden country, guided by distance clues

  2. 02

    Country Crunch: find the secret country from distance, compass direction, region, population and area clues

  3. 03

    Border Chain: build the longest chain of countries linked by land borders, with no repeats

  4. 04

    Higher/Lower and Stat Duel on population, GDP, life expectancy and other World Bank figures

  5. 05

    Capital Match with region-weighted distractors, Flag Frenzy, and Map Tap on real country shapes

  6. 06

    A Daily puzzle seeded by date and mode, so everyone gets the same round with no server

  7. 07

    Game Lab with 19 pitched modes, and a Data Explorer that shows the dataset's quality warnings

  8. 08

    Flutter phone version with 14 modes, including Density Duel, Compass Quest, Tongue Twist, Famous Foods and Landmarks

03 How it's built

Outside servicePublic country data (4 sources)
ServiceTS data build: cache, match, validate
DataLocal JSON + GeoJSON world dataset
AppReact web arcade
AppFlutter phone app
Appthree.js globe

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.