/ game-development / Fetching Free Player Faces for a Game Without Shipping the Wrong Human
game-development 4 min read

Fetching Free Player Faces for a Game Without Shipping the Wrong Human

I wanted real footballer photos on the cards in my draft game, free-licensed only. The fetch was easy. Not shipping the philosopher Socrates instead of the footballer was the hard part. Here is the pipeline and the disambiguation fix.

Fetching Free Player Faces for a Game Without Shipping the Wrong Human - Complete game-development guide and tutorial

My draft game, World 11, puts real footballers on cards. About 1,234 of them, pulled from World Cup squads going back to 1966. I wanted the cards to show the actual player, because recognising the face is half the fun of a drafter. What I was not going to do was ship copyrighted press photos. So the rule was simple: free-licensed images only, properly attributed, or a clean fallback. Getting the images was a morning. Making sure each one was actually the right person took longer and taught me more.

The Easy Part

Wikipedia and Wikimedia Commons host a lot of player photos under licenses you can actually use: Creative Commons, public domain, and similar. The MediaWiki API will hand you a page's lead image and the image's license metadata. So the first pass is short:

  • For each player name, hit the API for the page's thumbnail and the image's license.
  • Keep the image only if the license is explicitly free. Anything fair-use or non-free gets skipped, never downloaded.
  • Crop to a square, save it under a slug derived from the name, and write an attribution line crediting the photographer, because Creative Commons requires it.

Players with no free image get a clean initials badge instead. That is the whole happy path, and it covered a big chunk of the roster on the first run.

Then I looked at the cards.

The Hard Part, Names Are Ambiguous

The midfielder Socrates is one of the most famous Brazilians to ever play the game. Search Wikipedia for "Socrates" and you get the Greek philosopher. My fetch dutifully put a marble bust on the card.

It was not a one-off. "Hulk" gave me the comic-book character instead of the Brazil forward. A backup keeper named Luis resolved to an American businessman. A one-word name is a coin flip, and the exact-title lookup confidently grabbed whatever the most prominent article was.

The instinct is to fix it by searching " footballer" instead. That helps for the famous ones, but it introduced a new failure: for ambiguous backup players, the football-specific search returned the most famous footballer with that name, not the squad player I meant. Searching "Carlos footballer" gives you Roberto Carlos, who was very much not the reserve goalkeeper named Carlos I was after. I had traded a wrong philosopher for a wrong legend, which is arguably worse, because it looks plausible.

The Fix, Verify Then Decide

What actually worked was a verification pass that does not trust either lookup blindly:

  1. For each face, check whether the source page is actually described as a footballer. The page's short description or its categories tell you this cheaply. A philosopher, a businessman, a comic character all fail the check immediately.
  2. If a face fails, try to re-resolve it through a football-specific search and re-verify the result is a footballer.
  3. If it still cannot be confirmed, or the name is so ambiguous that the search returns an obviously different person, revert to the initials badge.

That third step is the one that matters. A missing face is fine. A clean initials card reads as a deliberate design choice. A confidently wrong face, a philosopher or the wrong famous player, breaks the illusion and makes the whole roster feel untrustworthy. So when in doubt, ship nothing rather than ship wrong.

There was one more gotcha worth flagging for anyone batching this. The obvious way to check "is this a footballer" is to read the page's categories, and the obvious way to do that fast is to batch fifty pages per API call. The API caps how many categories it returns per request, so most pages in a big batch came back looking category-less and got flagged as not-footballers. My first audit flagged 849 of 1,034 faces, including Messi. The page short-description field returns one clean string per page with no such cap, and switching to that dropped the false positives to a handful of genuinely wrong ones.

Where It Landed

After the verification pass and the reverts, coverage sat around 84 percent of the roster with confirmed-footballer photos, and the rest on clean initials. Every photo is credited in an attribution file that ships with the game. No press photos, no wrong humans, no marble busts.

The general lesson travels beyond games. When you are pulling identity-bearing data from a public source by name alone, the fetch is never the hard part. Disambiguation is. Build the verification step first, and make "show nothing" the default whenever you cannot be sure.

You can see the faces, busts safely excluded, in World 11, free in the browser.

Built by Kevin

Like this? You'll like what I'm building too.

Two ways to support and get more of this work.

Desktop App

HEARTH

A privacy-first Life OS for your desktop. Journal, tasks, and notes that stay on your machine. Coming soon, direct download from this site.

Read more
Digital Products

MY TOOLKITS

Receipts-first toolkits for shipping after hours, building Claude agents, publishing on Amazon, and more. The exact methods I used, not theory.

Browse on Whop

Need This Built?

Kevin builds products solo, from first version to live. If you want something like this made, work with him.