Pidgin

A real-time multiplayer party word game with an LLM-powered "warmth" meter


Role: Founder, Designer & Developer

Project: Independent (playpidgin.com)

Stack: Vanilla JavaScript, Firebase Realtime Database, Cloudflare Workers, Workers AI (Llama 3.3 70B)


Overview


Pidgin is a party word game I designed, built, and run in production at playpidgin.com. One screen hosts the game, a TV or a laptop everyone can see, and each player joins from their own phone by opening a link and entering a four-letter room code. No install, no app.


Each round, one player draws a secret prompt and has to get the room to guess it, but they can only describe it using a hand of random words they were dealt. They cannot speak or write freely. Everyone else races to type the answer.

Behind it sits roughly 1,500 curated prompts across eight categories, real-time state sync across every device in the room, two scoring modes, two clue modes, and a live semantic scoring service.


The problem


In a game like this, the fun lives in the near miss. Guessers are constantly almost right, and they need to feel that. So the game shows a live "warmth" score from 0 to 100 on every guess, plus a running list of the five closest guesses in the room. Getting that number to feel correct turned out to be the hardest problem in the project:


  • The original version used sentence embeddings to score how close a guess was.
  • In a live game it scored the nonsense guess "grinch bobson" at 78 out of 100, while a genuinely close answer scored 57.
  • The nonsense string happened to contain a word from the answer, and that was enough to fool it.

Players noticed immediately. Once the number is wrong once, nobody trusts any of the other numbers.


The solution


The useful insight was not "use an LLM." It was that the feature had been asking one system two completely unrelated questions:


  • A string question: is this the answer, just misspelled?
  • A world-knowledge question: how related are these two things?


Those need different tools. I split them.


The string half became a deterministic fuzzy matcher using Damerau distance with normalization for punctuation, articles, word order, and common typos. It is instant, free, testable, and covered by a test suite.


The semantic half became a separate Cloudflare Worker exposing a JSON API, backed by Workers AI running Llama 3.3 70B. The host device batches unscored guesses, keeps one request in flight at a time, and writes the returned scores back into Firebase so every client renders them.


What I did


  • Designed and shipped the product. Concept to live domain, including scope, feature prioritization, and release decisions across roughly a dozen iterations driven by real play sessions.
  • Architected the scoring split. Diagnosed the root cause and separated the deterministic matcher from the semantic service.
  • Integrated the LLM service. Prompt design and iteration, model selection against cost and latency constraints, defensive parsing of unreliable model output, CORS handling, and a graceful fallback to a local scorer when the service is unavailable.
  • Designed the game systems. Scoring economy, category system, round length, mid-game joining without losing scores, text to speech that reads clues aloud on the big screen, and an end-of-game board that resurfaces the funniest clues by player vote.
  • Ran the operations. Domain registration, DNS, proxied hosting, redirect rules, serverless deploys, and service bindings.


Key design decisions


  • A nondeterministic model never decides who wins. The split meant the model only ever influences a feel-good progress number. Correctness stays with the deterministic matcher. That boundary is deliberate.
  • The reward attaches to elapsed time, not clue count. Games were running long. The obvious fix was to reward clue givers for solving in fewer clues, and that is wrong. It rewards withholding clues, so clue givers go quiet and think, and you get more dead air rather than less. The behavior I actually wanted was tempo, so every second of hesitation costs you and firing off clues quickly is the winning strategy.
  • Knowing when to stop tuning. The LLM scoring went through several rounds of prompt fixes, and each one traded one bias for another. Everything collapsing to 0 or 100, then plausible near misses scoring at zero, then generic descriptions scoring too high. That pattern is diagnostic. When every fix creates a new failure elsewhere, you are asking a model for judgment it does not reliably have, and the answer is a different decomposition, not another prompt rule.


Impact


  • Live in production at playpidgin.com with real players.
  • Roughly 1,500 curated prompts across eight categories, with real-time sync across unlimited phone clients.
  • The scoring rearchitecture made the closeness feature trustworthy enough to keep, after the first version was actively breaking player trust.


What I learned


  • When a feature keeps failing, check whether it is secretly two features.
  • Incentive design is second-order. The obvious reward often produces the behavior you were trying to eliminate.
  • Directing AI tooling well is a design skill. The architectural call, the rejected scoring model, and knowing when prompt tuning had stopped paying off were all judgment, not code.