← back to the collection

Case study · NO. 05

Born in Madrid, plays for Morocco.

Half of Morocco's squad was born in the Netherlands, France, or Spain. Switzerland maps the Balkans and West Africa. That gap — between the badge and the birthplace — is the whole game.

Started
Summer 2026
Format
Five players, daily
Map libraries
None — canvas + d3
Accounts
None

The hook is a gap

A geography game about footballers sounds like a quiz: name the capital, win the point. The version worth building lives in the gap between where a player represents and where he was actually born. Achraf Hakimi came through Real Madrid’s academy and chose the Atlas Lions over Spain. Alphonso Davies was born in a refugee camp in Ghana to Liberian parents and became Canada’s best player. Every one of those is a small history lesson, and the game’s job is to make you guess, then reveal the truth. The round deliberately shows almost nothing — just the player’s face and name, no flag and no position — so you can’t reverse-engineer the birthplace from the badge. The nation, and the gap between it and where he was actually born, only lands at the reveal.

The answers didn’t exist yet

There was no dataset of footballer birthplaces to hand — so I built the pipeline to mine one. Every player’s Wikipedia title resolves to a Wikidata entity, that entity’s “place of birth” resolves to a town, and the town resolves to coordinates and a country. The interesting part was the failure modes: bare names like “Rodri” hit disambiguation pages, so a search fallback recovers them; the Netherlands’ ISO code is flagged deprecated on Wikidata, which quietly nulled the flag for four Dutch-born players who are exactly the diaspora stars the game wants; and Wikidata insists Dani Olmo was born in Zagreb, where he actually just moved as a teenager. This is the kind of grind-it-out data work I lean on coding agents for now — Claude Code wrote the Wikidata client and the batching while I made the judgment calls in a hand-checked overrides file, player by player. Four hundred-odd players resolved; the pool is curated by hand from there, with the born-abroad players deliberately weighted into the harder rounds so they’re never wasted as gimmes.

A globe, from scratch, inside a locked box

The globe is one <canvas> and about a kilobyte of maths — no map SDK, no tiles, no three.js. The site’s content-security policy forbids third-party scripts, so every pixel has to come from our own origin: a vendored world atlas, an orthographic projection, and the standard trick for dragging a sphere without letting it tilt off its axis. It reads the theme’s colours off CSS variables so it recolours the instant you flip dark mode, and it only spends CPU while something is actually moving. The reveal is the payoff — the camera glides to frame your pin and the truth together, an arc draws between them, and the real city drops in under the badge you were fooled by.

The Grassroots globe: an orthographic world map centred on Africa and Europe, with Lisandro Martínez's face and name above it and a 'Tap the globe to pin' prompt below
One canvas, a vendored atlas, and about a kilobyte of maths — no map SDK. Just the face and the name up top; the badge waits for the reveal.

Shareable, not spoilable

Like the rest of the collection, Grassroots keeps no accounts and no server state: the day is the same five players for everyone, computed from the date alone, and your whole result packs into a short link. The one rule the share had to respect is that it can’t spoil the day for the group chat — so the unfurl card is deliberately just a faint world map with the wordmark and your score over it. The map you actually have to solve stays on the page, where it belongs.

A Grassroots result screen scoring 878 out of 1000 across five players — Alisson, Vitinha, Lisandro Martínez, Marcus Rashford and Gregor Kobel — each with a distance and points, above Share result and Stats buttons
The whole day packs into a short link — score and distances, no accounts, no server. The five players are the same for everyone, computed from the date alone.