CCasey White
← All work
Duck Hunter HQ logoLive

Duck Hunter HQ

Find the ducks. Sign the log. Rule the fleet.

Scan a rubber duck, sign the log, level up. A QR-powered treasure hunt that turns cruise ships into a global game.

Category
Game / PWA
Timeline
1 week · 289 commits
Focus
Consumer AI
Visit Duck Hunter HQ ↗
Duck Hunter HQ preview
01

Why I built it

There is this wholesome real-world trend where cruise passengers hide little decorated rubber ducks around the ship for strangers to find. It started in 2018 on the Carnival Breeze and grew into a Facebook community of nearly 300K people, a physical thing with a big community but no digital home. So I built one. You find a duck, scan its QR, see the profile the hider made, and sign the log. From that one loop I layered on collections, RPG leveling, ship-vs-ship leaderboards, a spinning 3D globe, and an arcade mini-game. Pure joy to build.

02

What it does for people

  • Scan-and-sign-the-log core loop: find a duck, scan its QR, see the profile, log the catch, a couple of taps
  • Zero-friction anonymous sign-in, so a finder logs a catch instantly and can add their email later without losing history
  • RPG progression with XP, auto-levels, rarity tiers, badges, and power stats on every duck
  • Ship-vs-ship fleet leaderboards so whole cruises compete against each other
  • A duck designer and minting flow where hiders customize and print their own duck's QR card
  • Offline Cruise Mode, because ship wifi is genuinely terrible. The whole thing keeps working at sea and syncs when you reconnect
03

Under the hood

  • Built on the bleeding edge: Next.js 16 App Router with React 19, using the new proxy.ts convention instead of middleware
  • Offline-first architecture using IndexedDB queues. Finds and duck-arming get captured offline and drained when wifi returns, fully unit-tested with fake-indexeddb
  • A three-tier Supabase client setup (browser, cookie-bound server with RLS, secret admin) with auth via getClaims rather than getSession
  • Postgres does the heavy lifting: generated columns for levels, a materialized fleet_leaderboard view refreshed on a schedule, and RLS enforcing owner-scoped writes
  • An anti-cheat integrity layer: non-enumerable opaque duck codes, rate-limiting, burst-flagging, and down-weighting of unconverted anonymous finds
04

The cool bits

  • A genuine physical-meets-digital product. The QR code on a rubber duck is the heartbeat of the whole app
  • A 3D interactive globe (react-three-fiber and r3f-globe) plotting where ducks get found around the world in real time
  • A bold neo-brutalist pixel design system called Chromatic Quest: sharp corners, hard-offset shadows, no white, retro-arcade energy
  • Server-side PDF and print generation for the physical duck cards (pdf-lib and resvg), closing the loop back to the real world
05

How long it took

Roughly one week of very intense building.

1 week · 289 commits

06

What I worked through

The biggest challenge was cruise wifi. It is genuinely unreliable, so I could not assume a connection for the core experience. That pushed me into a real offline-first architecture with IndexedDB queues that capture finds while offline and sync cleanly when you reconnect. The other tricky part was integrity: because anyone can scan and the app is anonymous-first for zero friction, I had to design against cheating without adding friction, so opaque non-enumerable duck codes, rate-limiting, burst-flagging, and down-weighting finds from anonymous users who never convert.

07

The full stack

Next.js 16 (App Router, proxy.ts)React 19TypeScriptTailwind CSS v4Supabase (Postgres, Auth, RLS)Three.js via react-three-fiber + drei + r3f-globeTopoJSON / world-atlasStripe@yudiel/react-qr-scanner + qrcodeIndexedDB (offline sync)pdf-lib + resvg (print generation)PostHogNodemailerVitest + Testing Library (fake-indexeddb)Vercel (cron)