/ recent work / dungeon-jerks
Dungeon Jerks
Snarky tile-and-card board game on the web — pass-and-play or async multiplayer, 110 tiles, pre-generated narration, CPU bots that wait their turn.
A web-playable version of Adam’s PG-13 party board game — “D&D for ADHD” energy, 3–8 players, tiles you land on, characters with abilities, and action cards that read like a roast. The site is the game: not a landing page with a download link.
The problem
The physical game works at a table. Getting eight people in one room with the box is the hard part. We wanted a version that runs in a browser, feels like the cardboard experience (dice, narration, character art), and doesn’t require everyone to install anything.
The approach
SvelteKit + committed content. Tiles, characters, and actions sync from Airtable at build time into JSON — no live CMS calls in the hot path. Art lives in static/art/ (~294 WebP frames). Audio lives in static/audio/ (~1600 MP3s) with on-demand TTS only for roll outcomes that weren’t pre-baked.
Pass-and-play first, async second. v1 is one device passed around the table. v2 shipped: token-in-URL multiplayer via Netlify Blobs (/api/games), 3s polling, canonical GameState on the server while animations and dice stay client-side.
Typed effect atoms. HP, GP, move, statuses, gags — parsed into a small catalog so card text isn’t a pile of special cases. Roll tables use ranges (1–5 / 6–15 / 16–20), not twenty unique rows.
What we built
- 110 tiles, 89 characters (heroes, villains, NPCs), 105 actions on a 2D grid keyed
"x,y". - CPU bots with roll-table decisions, special abilities, and pacing that waits for narration to finish before the next bot acts — otherwise async games feel like a slot machine.
- SEO layer: VideoGame + FAQ + Breadcrumb JSON-LD, changelog RSS, IndexNow under canonical
www, apex 301s to www. - Vitest coverage on store logic and extracted
nextBotAction— character-specific tests boot with fixed seeds. - Marketing roster art pipeline: crop to bridge ratio (2.25:3.5), match scripts from Dropbox → repo, optional R2 mirror.
Trade-offs
Pre-generating narration costs repo size and build time. It buys instant tile read-aloud without hammering ElevenLabs on every landing. Dynamic TTS stays behind /api/tts for the long tail of roll lines.
We killed the Supabase plan early. Blobs + polling is boring and shippable; websockets would have been a second product.
What’s ongoing
Status effects are partly stubbed — pills render, full mechanical hooks still landing. Gold transfer between players is a v1.1 extension. Art gaps are tracked in-repo (docs/missing-art.md). Deploys are gated: push to main only when Adam says go — Netlify deploys on every push.
Results
The game is live and playable at dungeonjerks.com. Recent sessions surfaced real UX wins: footer contrast to WCAG AA, slower bot cadence, canonical host cleanup. The funnel we care about is “open /play, finish a round, send the link to friends” — and that loop works.