Mapped asks how well you know the map. The world fills the screen, and you name every country, click the one it asks for, or name the one that lights up, across three topics: Countries, Flags and Capitals. Anyone can play without an account; signing in with Google saves your games and puts them on 63 public leaderboards. The interesting part is keeping those boards honest: the browser and the server run the same game code, and the server replays every finished game before it ranks it.

A dark world map filling the screen, most of the Americas, Europe, Russia, Asia and Australia filled in bronze, Brazil glowing gold, with a toast above the input reading 'Accepted as Brazil', a score of 55 / 197 and a By region card counting finds per continent
Fig. 1 World · Type, 55 countries in. The last guess was typed as “brazl” and accepted on Enter, because no other name is that close. Greenland stays dark: territories take their owner's colour and fill in with Denmark.Screenshot of mapped.lukeghanna.com at 1440×810, 15 October 2026. The names were typed by a script, so the clock reads 0:09; game starts were blocked, so the game ran offline and nothing reached the live database.

Forgiving, never wrong

Sporcle's Countries of the World, the quiz Mapped started from, matches names strictly, so a missing accent or "St." for "Saint" costs you a country. Mapped normalizes every guess first: accents, case, punctuation and the words "the", "and" and "of" are dropped, so "St. Kitts & Nevis" and "saint kitts and nevis" are the same key. Exact names, and 55 aliases from USA to Holland to DRC, count the moment they are typed.

Typos are only forgiven on Enter, and only when one name is that close. A name of 5 to 9 letters may be one edit away and a longer one two; anything shorter must be exact. The candidate has to be the only country or territory in the whole index within range, which is what keeps Iran from becoming Iraq, Austria from becoming Australia, and Niger from becoming Nigeria. "Niger" itself waits: while Nigeria is unfound, the game holds the match for 600 ms in case you are still typing.

Hints are there in every game, and every one counts against you. A board ranks by most found, then fewest hints, then fastest, so an unfinished run still ranks behind everyone who found more.

Nine games from one table

A topic changes what you are shown and how you answer; the answer is always a country, so the engine barely changes. One rules table gives each of the nine games three facts: whether targets come one at a time from a seeded queue or in any order, what the prompt is, and how you answer. The reducer, the input, the prompts and the hint ladders all read those facts instead of branching on names.

TypeLocateIdentify
CountriesName them all, any orderGiven a name, click the countryA country lights up; type its name
FlagsA flag; type its countryA flag; click its countryA country lights up; pick its flag from 4
CapitalsA named country; type its capitalA capital; click its countryA named country; pick its capital from 4

The wrong flags are chosen to be hard. 37 hand-made groups hold the flags people confuse, from Chad, Romania, Andorra and Moldova to the Nordic crosses, and Flags · Identify takes decoys from the target's groups first, then its subregion, its continent, and anywhere. Capitals · Identify does the same with the target's neighbours, so Kampala's decoys are capitals of countries Uganda borders. Both are seeded by the game and the target, so a reload shows the same four.

Chad glowing gold in the middle of a dark world map, and below it a card reading 'Pick its flag' with four blue, yellow and red vertical tricolours: Moldova's with an eagle, Chad's, Andorra's with a crest, and Romania's
Fig. 2 Flags · Identify on Chad. All three decoys come from Chad's lookalike group; a wrong pick costs one of three tries and briefly lights up whose flag it was.Screenshot of mapped.lukeghanna.com at 1440×810, 15 October 2026, with the app's ?seed= test parameter choosing an order that starts on Chad. The game ran offline.

Capitals needed a second try. It launched asking for every capital in a region from memory, and its first two games found about 1 per cent. Now Capitals · Type and Identify name the country and light it up, and only Locate, which gives a capital and asks you to click its country, stays hard. Runs under the old rules stay in their owners' history as "Unranked: older rules" and left the boards.

The server replays every game

A leaderboard where the browser reports its own score is a leaderboard of edited requests. So the game engine is one pure reducer with no DOM and an injected clock, imported by both the React app and the Worker, and every action the player takes is logged with its time.

Start
the server picks the seed and stamps the start on its own clock; if it takes over 1.5 s, the game plays offline and unsaved
Play
the browser shuffles the pool with that seed; every guess, click, skip and hint goes into the log with its time
Finish
the server stamps receipt, rebuilds the start state and runs the log through the same reducer
Rejected
malformed, an action that changes nothing, anything after the end, or no end
Unranked
custom regions, nothing found, faster than 300 ms a country or 100 ms between finds, or a log clock over 3 s short of the server’s
Ranked
the replayed result stands; it replaces your best only if it beats it
Fig. 3 One game from start to the board.Drawn from worker/games.ts, worker/replay.ts, src/game/reducer.ts and src/game/ranking.ts.

Found, hints, time and how the game ended all come from the server's replay, never from the browser. The seeded order had one trap. The pool used to be sorted by name with localeCompare, whose order depends on the runtime's language, so a browser set to another language could shuffle a different order from the same seed than the Worker did. It is now sorted by country id with a plain code-unit comparison.

Pause was cut for the same reason. It let a player stop the clock and look answers up in another tab, so now the clock always runs: Esc asks "Give up?" and the clock keeps going while it asks. The reducer still understands a pause, so a log that contains one is marked unranked rather than trusted.

The known gap is written down: a bot that submits a made-up log at a believable human pace passes. There is no admin screen; removing a run is a D1 delete and a script that rebuilds the bests.

A Leaderboards card over the dimmed map with the Flags tab selected among Countries, Flags and Capitals, the Type tab among Type, Locate and Identify, the World chip among the region chips, and a table with one player: Luke Hanna, 195 of 197 found, 334 hints, 1:14:43, Oct 14
Fig. 4 The World · Flags · Type board, with one player. Flags is one of the three topic tabs above the mode tabs and region chips. Boards are public and need no sign-in; only the display name a player picks is ever shown.Screenshot of mapped.lukeghanna.com/leaderboards/flags/type/world at 1440×810, 15 October 2026, signed out.

Play first, sign in later

An account is never needed to play. A game started signed out gets a claim token, which the browser keeps for 90 days, and the server keeps the finished game as long. Finishing offers "Sign in to save this run", with the rank the run would have had, and signing in on that browser claims every game it played since. Bests saved in the browser before accounts existed come along too, marked unranked because nothing verified them.

Sign-in is Google's OpenID Connect flow, done entirely on the server with PKCE, so there is no Google script on the page and the content security policy stays strict. Sessions, claim tokens and IP addresses are stored only as HMACs.

The whole thing runs on Cloudflare's free plan, and its limits shaped the code. The Worker runs only for /api/*; the app, the map and the flags are static assets, which cost no Worker requests. A whole-world replay stays under 5 ms in the tests, against a 10 ms CPU limit. D1 allows 50 queries per invocation, so claims go up eight at a time, and Your games, which shows a best and a rank for each of the 63 boards, computes every rank inside one query instead of one query per board, which used to blow through the limit.

Learn

Playing Flags well means knowing 197 flags, so Flags has an opt-in Learn switch on the setup card. It is a drill, not a game: no timer pressure, no hints, no board, and nothing sent to the server. A flag appears with four names below it, lookalikes again. A miss shows the right answer and that flag comes back three cards later; two right in a row and it is learned and leaves the round. Learned flags are kept in this browser, fill in on the map as they are learned, and the next round asks only the ones still left.

A Learn round zoomed on Europe: learned countries filled bronze, North Macedonia glowing gold with its name, a large North Macedonia flag over the answer 'North Macedonia · Skopje' and four name buttons, and a top bar reading Learned 26 / 45, Right 95%, Wrong 4
Fig. 5 Europe · Flags · Learn, 26 of 45 learned. North Macedonia has just been answered right a second time, so it glows as it fills in.Screenshot of mapped.lukeghanna.com at 1440×810, 15 October 2026. The answers were given by a script reading the page, four of them wrong on purpose.

Learn is its own small reducer beside the game's, deterministic for a seed, and its four choices are reseeded for every card asked, so a returning flag's options move and you learn the flag rather than the button.

Status

Live at mapped.lukeghanna.com, built from 5 to 14 October 2026 in four specs: the map and the three modes, then accounts and leaderboards, then Flags and Capitals, then hints, easier Capitals and Learn. 197 countries drawn from Natural Earth 1:50m outlines at full detail in a 678 KB file, with 56 territories drawn but never guessable, and self-hosted flag SVGs so the page makes no third-party requests beyond Cloudflare's analytics. Light and dark themes, keyboard play throughout, and a phone layout where the cards become bottom sheets. 341 Vitest tests across 31 files, including Worker tests against a real local D1, and 40 Playwright tests (35 desktop, 5 on a Pixel 7 profile) against the real Worker with a fake sign-in that only works on localhost.

At a glance
Countries
197
Games
3 topics × 3 modes
Leaderboards
63, replay-checked
Flag lookalikes
37 groups
Worker runs for
/api/* only
Tests
341 unit · 40 e2e