Inspiration

We wanted to make a game that felt stupid to play in the best possible way. Not stupid as in broken — stupid as in the moment your friend watches you slowly lean your head to the left to dodge an incoming egg, you both burst out laughing.

The core idea hit fast: what if the controller was your body? No keyboard, no gamepad — your face moves the character, and your voice is the attack button. That absurdity is the game. We called it Duck Trouble, a 1v1 browser fighting game where one player is a Duck and the other is a Goose, and you honk each other to death.

We also wanted zero friction to play. No app to download. No account. You run a script, share a four-letter room code, and you're fighting. That simplicity shaped every technical decision we made.


How We Built It

The control scheme

  • Movement: Uses MediaPipe Face Mesh running entirely in the browser via CDN. Every frame, we track the nose tip position (landmark #1) and compare it against a calibrated center point. Leaning or nodding past a threshold moves the avatar one tile on a 10×6 grid.
  • Attacking: Uses the Web Audio API. The microphone feeds into an AnalyserNode that computes RMS volume every 50ms. Making noise fills a honk meter — when it hits 100%, an egg fires automatically. No button press required.

The server

The frontend is vanilla HTML + Canvas. All game logic lives in a Python FastAPI server running a server-authoritative game loop at 20Hz over WebSockets. Both clients send inputs (head direction + mic volume); the server owns the "truth" about positions, HP, eggs, and round state. This eliminated any possibility of the two browsers drifting out of sync.

Powerups and terrain

Each round generates a random set of wall tiles (hay bales). We run a BFS pathfinding check to guarantee a clear path exists between spawn points before accepting any wall layout. Powerup crates spawn every 5–10 seconds:

  • Shotgun: Fires 3 eggs across 3 adjacent rows simultaneously.
  • Boomerang: Travels forward then returns, hitting on both passes.
  • Bomb: Slow projectile that explodes on impact, dealing AOE damage and destroying a 3×3 tile area of walls.

Claude at match end

When a match ends, we make one call to the Claude API (claude-3-5-sonnet) with the winner, loser, and biggest single-hit damage dealt. Claude returns 2 sentences of bird trash talk in under 35 words. The message is read aloud via the browser's Web Speech API. If the API call times out, we fall back to hardcoded messages.


Challenges

Camera sensitivity — harder than it sounds

Head tracking sounds simple until you actually play-test it. Our first pass fired movement events continuously while your head was tilted, which made the avatar slide uncontrollably. We fixed this with an _inThreshold flag: a direction only fires once per threshold crossing. The player has to return to center before the next move triggers.

Threshold tuning took the most iteration:

  • ±8% of frame width for left/right.
  • ±6% of frame height for up/down.
  • 400ms cooldown between moves.

Audio sensitivity — every room is different

The mic challenge is that ambient noise varies wildly. Someone in a quiet library and someone in a noisy office start from completely different baselines. On first launch, we sample the microphone for 3 seconds to set a baseline, then subtract it from every reading. We also apply a 3x sensitivity multiplier so quiet honks still register.

Design — knowing what to cut

The hardest design question wasn't "what do we add?" — it was "what do we not add?" Our initial spec had character-specific abilities, stamina, and a spectator mode. We cut all of it.

The game became better the moment we treated movement and dodging as the same system. There's no dedicated dodge button — you just step off the tile. Playtesting showed rounds lasted 4–8 attacks naturally, which felt like the "sweet spot" for chaotic fun.


What We Learned

  • Physical interfaces create comedy that no game mechanic can manufacture on its own.
  • Server-authoritative logic at 20Hz is the right call for real-time browser games, even for small projects.
  • Calibration matters more than sensitivity tuning — getting the baseline right makes the controls feel fair in any environment.
  • The best design decisions are often the ones where you remove a feature and the game gets more fun.

Built With

Share this project:

Updates