When my wife and I are on our phones, my daughter wants in. Not to watch a video, but to play with us, the way a multiplayer game works: everyone on their own screen, all in the same game. She is four, so it had to work without reading, with big buttons, and it had to be something she could actually win.
So I built one with Claude. This post is the story of how it started, how the people playing it shaped every feature, and a short look at how it works under the hood.
It started with one prompt
This was the whole first prompt:
let's create a fun applications multiplayer that we can play with my kid 4 years old and wife what do you suggest?
Claude came back with a few ideas that fit a four-year-old: a shared drawing board, a bubble-popping game, an animal sounds game, and a treasure hunt. I picked the treasure hunt. The phone shows a color or a shape, says it out loud, everyone runs around the house to find one, and taps Found it! Every find fills a shared star meter, and confetti plays when the family reaches the goal.
That first version was playable the same night. It was not the game we play now. Everything after that came from watching people play.
Then the players started asking for things
My job became simple: play, watch, and write down what people asked for. Then I described each change to Claude in a sentence or two. These are the requests, roughly in the order they came:
- Taps did not show on the other phone. A tap on one phone did not show up on the others. I moved the game to Netlify: one page plus a small Netlify Function that keeps the shared game in Netlify Blobs. Every phone checks for changes about every second and a half.
- Taps felt dead on mobile. A tap gave no visible feedback. Buttons now press down, and tapping a picture stamps a big green check on it.
- Make it a real game. A small color swatch instead of a giant one, six pictures per stage with wrong answers mixed in, a host who controls the game, a Next button that appears only when everyone is done, 10 to 30 stages, and a ranking from first to last at the end.
- Everyone starts at the same time. Each stage now opens with the voice saying what to find, then a 3-2-1-GO countdown that lines up on every phone. The pictures stay face-down until GO.
- Only pictures nobody can argue about. We removed every picture that could be argued: a crab that looks orange, a spider that looks grey. A square is never a wrong answer in a rectangle round.
- More than colors and shapes. A game picker on the start screen, plus Animals (sea, birds, farm, safari, bugs) and Flags. Flags is a matching game: the flag shows during the countdown, and you find the same one, so a four-year-old can play it too.
- No guessing. Two wrong taps in a row and you are out for that stage, no stars. A right tap in between resets the count.
- No peeking. The countdown cover was slightly see-through. Now it is solid.
- Less waiting. The host can skip ahead 10 seconds after GO, and can remove a player who left.
Not every idea stayed. We tried a second game, Color Spy, where you find a real object in the room and the host approves it. We removed it. Taking things out is part of the process too.
How it works
The whole game is one HTML page and one serverless function. There is no build step and no database server.
-
One page, one API. Every phone loads the same page and talks
to
/api/game, a Netlify Function. The function stores the game in Netlify Blobs: the current stage, who joined, and who finished each stage. -
One key per action. Blobs is last-write-wins, so two phones
writing the same record at once could overwrite each other. Instead,
every action is its own key, for example
res/<game>/<stage>/<player>~<wrong taps>~<time>. Nothing is ever read, changed and written back. - The same stage on every phone, from a seed. The server only stores a random seed. Each phone builds the stage from it with the same shuffle, so all phones show the same six pictures in the same order without the server sending them.
- A countdown that lines up. When the host starts a stage, the server stamps one start time. Each phone estimates how far its own clock is from the server's and counts down to that moment. I tested it with one phone's clock set 8 seconds wrong, and both still said GO together.
- Voice and sound without files. The spoken prompts use the phone's built-in speech, and the sound effects are generated with the Web Audio API. No audio files to load or license.
Design decisions
The rules were shaped by who was playing: a four-year-old and a group of competitive adults, in the same game.
-
No reading required
The voice says what to find, the target shows as a color, shape or flag, and every button is big. A four-year-old can play every stage.
-
Fair for kids and adults
Everyone starts at the same GO, pictures stay hidden until then, and guessing is punished. Speed decides the stars, not who peeked first.
-
The host runs the game
One phone hosts. Only the host moves to the next stage, and only when everyone is done or the wait runs long. The host can also remove a player.
-
Only pictures nobody can argue about
Every picture belongs to exactly one group. I checked about 63,000 generated stages to make sure each one has the right number of correct answers and only clearly wrong ones.
Practical examples
Simplified example. The snippets below show the two techniques that keep every phone in the same game. They are not the full production code.
1. Build the same stage on every phone from a seed
// Small seeded random number generator (mulberry32)
function rng(seed) {
let a = seed >>> 0;
return () => {
a = (a + 0x6d2b79f5) >>> 0;
let t = a;
t = Math.imul(t ^ (t >>> 15), t | 1);
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
function shuffle(items, random) {
const a = items.slice();
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}
// Every phone gets the same seed from the server, so every phone gets the same order.
shuffle(["๐", "๐", "๐ธ", "๐ณ", "๐", "๐ท"], rng(seed + stage));
2. Count down to the same moment on every phone
let offset = 0, best = Infinity;
async function syncClock() {
const sent = Date.now();
const { now } = await (await fetch("/api/game")).json(); // server time in ms
const received = Date.now();
const roundTrip = received - sent;
if (roundTrip < best) { // the fastest round trip is the most accurate
best = roundTrip;
offset = now - (sent + received) / 2;
}
}
const serverNow = () => Date.now() + offset;
const msUntilGo = (goAt) => goAt - serverNow(); // 3000..0 is "3, 2, 1", then GO
Conclusion
What started as my daughter wanting to play with us turned into a game my workmates play too. I did not plan most of its features. The players did, one small request at a time, and Claude turned each one into working code.
The takeaway: start with something playable, put it in front of the people who will use it, and let them tell you what to build next.
What I would do next
- Game rooms. Right now there is one shared game. A short room code would let several groups play at the same time.
- A Filipino option. The voice and the words in Filipino (Pula, Asul, Dilaw), so my daughter hears both languages.
- More games. The game picker is ready for them. Paint Splash and Color Hide and Seek are next on the list.
References and further reading
- Claude by Anthropic
- Netlify Functions documentation
- Netlify Blobs documentation
- Web Speech API (MDN)
- Web Audio API (MDN)
- Related on this site: How to transcribe confidential audio offline with Whisper and CTranslate2