/ recent work / dnd-chat
dnd.chat
Gameplay-first campaign chat — auditable dice, DND Cards conjures, initiative and HP beside the thread, session RSVPs and reminders from table@dnd.chat.
Campaign chat built for rolls, not side-channel Discord math. Same Supabase account as DND Cards — campaigns, cards, characters, and members stay in sync. The tavern is the table: dice log to roll_events, /summon goblin embeds cards from the deck, combat state rides beside the thread until you open the VTT.
The problem
DND Cards became the DM’s board — Kanban, vanity URLs, card-native VTT. Players still needed a place to talk during play without exporting trust-me-bro rolls to a general channel. Discord works until initiative order and HP live in three different tabs.
We wanted chat that respects campaign roles (DMs see statblocks; players don’t get AC/HP/CR in embeds), shares auth with the grimoire, and treats dice as first-class data — not pasted text.
The approach
Shared database, chat-specific tables. One Supabase project with dndcards. dnd.chat adds chat_*, roll_events, reactions, threads, and feature votes. Campaign theme drives tavern skin; campaign_members.role gates whisper ACL and DM Mode on embeds.
Slash commands as gameplay. /roll 1d20+5, /r adv 1d20, /summon, /w whispers — parsed server-side, stored auditable. Card embed buttons proxy to dndcards’ deriveActions engine so attacks in chat match the VTT action bar.
Channels that match play. IC table talk, OOC, lore, loot, session plans — not one endless #general. Virtualized message list, threads, reactions, pinned cards, mobile-safe toolbar.
What we built
- Tavern UI — character switcher pill, slash command palette, 3D dice with brand-matched materials (Three.js), MP3 SFX, optional ElevenLabs narration + prebaked audio pipeline.
- Combat hooks — initiative beside chat, auto damage rolls on successful attacks, spell saves, condition chips, bi-directional HP sync with character sheets.
- Session planning — schedule, RSVP, Resend reminders from verified
table@dnd.chat. - Attachments — Bunny CDN uploads for in-chat images.
- Marketing + SEO — guides (combat in text chat, Discord vs campaign chat), changelog, feature voting, compare pages, dynamic OG cards.
- Admin portal — 20 platform tools (users, campaigns, rolls audit, credits, flags, impersonation) behind shared auth.
- Cross-app handoff — magic links, grimoire character picker, dndcards creator deep links, Stripe checkout stub for AI credit metering.
Trade-offs
We deferred LiveKit voice, Discord mirror, and native VTT to roadmap stubs — chat ships first; maps stay on dndcards. Webpack production builds instead of Turbopack after flaky Netlify _next/static deploys. Polling and Supabase Realtime over a second websocket product.
Prebaked narration costs repo size. It buys instant table read-aloud without hammering ElevenLabs every roll line.
Results
Live at dnd.chat. The loop we care about: sign in once, pick a campaign, roll in-thread, conjure a card, whisper the rogue — same grimoire, no second login. Footer on dndcards links back to the tavern.
What’s ongoing
Discord bridge stub, AI session recaps API, M3 VTT handoff docs, Chrome extension polish for Beyond import paths. Deploy when Adam says go — Netlify on main, migrations through shared Supabase.