Skip to main content
Back to articles
6 min read WebGamesGeographySVG

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.