How I Built a Guess-the-Country Border Game
Building a free world-map quiz where you name 235 countries from their border shapes — the Robinson projection, SVG paths, zoom and pan, and a flag gotcha on Windows.
I wanted a small geography game with one specific twist: you guess a country from the shape of its borders, not from a flag or a satellite drop. Most “guess the country” games lean on flags or location. Borders felt like the harder, more interesting signal — and a good excuse to learn how world maps actually get drawn on the web. The result is Guess the Country, a free browser game with all 235 countries and territories. Here’s how it came together.
Borders as data, not an image
The first decision was to treat the map as data, not a picture. A flat PNG can’t highlight one country at a time, and that highlight is the whole game. So I started from Natural Earth’s public-domain admin-0 country dataset (the 50m layer), which gives every country as GeoJSON polygons in longitude / latitude. I simplified it locally and filtered out non-playable scraps, landing on 235 border features.
GeoJSON coordinates are geographic, though — degrees on a globe. To draw them on a flat screen you need a projection.
Projecting the globe with Robinson
I went with the Robinson projection. It isn’t equal-area or conformal; it’s the compromise projection that just looks like a world map to most people — the one you grew up seeing on classroom walls. That familiarity matters when the entire game is recognising shapes.
Robinson is defined by a lookup table of stretch factors at every five degrees of
latitude, interpolated in between. Each [lon, lat] becomes an {x, y} on a fixed
1000×540 canvas, and each country’s polygons turn into an SVG path string of
M … L … Z subpaths. Multi-polygon nations (think Indonesia or Canada’s islands) just
append more subpaths. I also compute each country’s centroid from its largest ring so the
“mystery” pulse marker lands on the mainland instead of floating in the sea.
The map that wouldn’t stop stretching
The first painful bug wasn’t math — it was one SVG attribute. The map distorted every
time the window resized: Greenland ballooned, Chile went stubby. The culprit was
preserveAspectRatio="none", which forces the viewBox to fill its container regardless of
proportions.
Switching to preserveAspectRatio="xMidYMid meet" fixed it instantly. Now the map keeps
its aspect ratio and letterboxes inside whatever space it’s given — it behaves like a
fixed image, but it’s still live vector graphics underneath, so per-country hit-testing
keeps working. That “feels like one image, acts like data” property is exactly what the
game needs.
Zoom and pan without breaking clicks
Tiny nations — Andorra, Tuvalu, the Caribbean — are unguessable on a world-scale map, so
the map needed zoom and pan. I wrapped all the map content in a single transform group and
drove it with a { scale, x, y } state object.
Two details took the most care:
- Zoom toward the cursor. Scaling around the origin makes the map lurch away from
where you’re looking. The fix is to convert the cursor to SVG user-space with
getScreenCTM().inverse(), find where that point sits inside the content group, then re-translate after scaling so the same point stays under the cursor. It makes zoom feel anchored instead of jumpy. - Don’t let a drag fire a guess. Panning and selecting a country are both pointer interactions on the same element. I track movement during a drag and only treat a pointer-up as a country click if it moved less than a few pixels. Above that threshold, it was a pan, and the click is ignored.
The wheel listener also has to be registered as non-passive, or the browser scrolls the page instead of letting the game zoom.
The flag gotcha nobody warns you about
In the country picker I wanted a flag next to each name. The obvious move is flag emoji — 🇧🇷, 🇯🇵, and so on. They work beautifully on macOS, iOS, and Android.
They do not render on Windows. Windows ships no flag glyphs in its emoji font, so the regional-indicator pairs fall back to showing the two-letter country code as plain text. Since I develop on Windows, I caught it immediately, but it’s the kind of thing that silently ships if you only test on a Mac.
The fix was flagcdn.com PNG images keyed by ISO alpha-2 code. The
Natural Earth data uses alpha-3 ids (TUR, USA), so I keep an inline alpha-3 → alpha-2
map. The handful of entities with no flagcdn flag — Northern Cyprus, Somaliland — fall
back to a coloured chip in their continent’s colour, which ties into the rest of the
palette anyway.
Making the continents the visual language
One small thing I’m happy with: every part of the UI speaks in continent colours. Solved countries fill with their continent’s hue, the legend uses it, the picker rows carry it as a left stripe, and when you reselect a solved border the panel reveals its flag, name, and a continent-coloured hint chip. None of that is essential to play, but it makes the board feel like one coherent object instead of a grid of controls.
What I’d reach for next
If I extend it, the obvious additions are a daily-puzzle mode, a flag-guessing mode (the flag images are already loaded), and difficulty tiers that hide the continent hint. But the core — guess the country from its border shape — is the part I set out to build.
You can play Guess the Country here. It’s free, runs in the browser, and saves your progress locally. If you beat it without zooming into the Pacific, I don’t believe you.