A dnd platform using chatgpt login. https://dnd.waylonwalker.com
Find a file
2026-10-12 08:47:20 -05:00
assets wip 2026-10-12 08:47:20 -05:00
k8s fix companion connection 2026-10-03 16:48:03 -05:00
scripts Add rules authority, scene continuity, and audio support 2026-10-04 01:04:11 -05:00
tests wip 2026-10-12 08:47:20 -05:00
.dockerignore init 2026-10-03 15:54:03 -05:00
.gitignore Exclude local session exports 2026-10-04 01:04:49 -05:00
CREDITS.md fix companion connection 2026-10-03 16:48:03 -05:00
dnd_chatgpt_server.py wip 2026-10-12 08:47:20 -05:00
dnd_localfirst_demo.html wip 2026-10-12 08:47:20 -05:00
Dockerfile Add rules authority, scene continuity, and audio support 2026-10-04 01:04:11 -05:00
justfile wip 2026-10-12 08:47:20 -05:00
LUNA_5E_RULES_HANDOFF.md Add rules authority, scene continuity, and audio support 2026-10-04 01:04:11 -05:00
LUNA_RULES_FOLLOWUP_HANDOFF (1).md Add rules authority, scene continuity, and audio support 2026-10-04 01:04:11 -05:00
README.md wip 2026-10-12 08:47:20 -05:00
THIRD_PARTY_AUDIO.md Add rules authority, scene continuity, and audio support 2026-10-04 01:04:11 -05:00

Lantern Table — local-first AI tabletop

Lantern Table is a local-first D&D/tabletop web app. The campaign, characters, transcript, timeline, drawings, image attachments, preferences, and player identity stay on participant devices. The homelab service serves the UI and coordinates peer discovery; active play uses WebRTC DataChannels wherever possible.

The production hostname in this package is https://dnd.waylonwalker.com.

What this iteration includes

Fast campaign start

The campaign wizard offers one-click presets for Heroic Fantasy, Creeping Horror, Story First, Tactical Dungeon, Mystery, and Rule of Cool. A host can optionally adjust difficulty, rules strictness, DM style, description density, and character-creation depth before opening the table. Everything remains editable later, and world preparation is intentionally lazy so players can start immediately.

Rich AI Dungeon Master

Lantern Bridge runs on the host device and streams DM responses into the browser. ChatGPT uses the Responses API; local Ollama inference uses Ollama's native model and chat APIs. Both feed Lantern's normalized text and tool stream. The host can:

  • suggest 2–5 next moves while preserving free-form play
  • request local dice rolls
  • render NPC, monster, item, quest, clue, faction, and location cards
  • propose reviewable campaign-state patches
  • change the table atmosphere with a theme + environmental effects
  • suggest polished prompts for scene, map, character, object, and handout art

The host AI Connections panel discovers Ollama, LM Studio, llama.cpp, ChatGPT, and the Lantern image engine. Ollama is the supported local DM adapter in this increment; the host can explicitly start it from the panel, select an installed model, view live-memory profile estimates, and run a local tool-call benchmark. Discovery never starts every installed runtime. ComfyUI is reported only as an advanced connector and is hidden from the normal connection list.

Lantern Table does not generate images in this build. Players can upload/paste their own images, attach them to scenes or characters, draw in the built-in canvas, and copy the DM's suggested image prompts into any image generator they prefer.

Atmosphere, motion, and themes

The app ships 78 theme choices spanning the light/dark families used around the existing sites and Theme Lab, plus gameplay-oriented seasonal/retro palettes. Themes include Rosé Pine, Catppuccin, Ayu, Nord, Gruvbox, Tokyo Night, Everforest, Kanagawa, Solarized, One, GitHub-like light/dark, Monokai, Modus, WhiteSur, Serika, Black Gold, Matte Black, Rhiannon Night, and many more.

The DM can call set_atmosphere at meaningful scene transitions. Its instructions describe when each family is appropriate and how to combine environmental effects without harming readability.

Environmental effects currently include:

  • snow
  • rain
  • dust motes
  • embers
  • fireflies
  • fog
  • lantern glow
  • bubbles
  • reflective water
  • underwater caustic feel
  • aurora
  • storm/lightning beats
  • stars

Motion is layered and adaptive: lightweight spring integration drives UI transitions, Canvas 2D handles particles, and a small zero-dependency WebGL layer handles water/underwater/aurora effects. The implementation follows the same production-friendly pattern that libraries such as Radiant demonstrate—visibility pausing, DPR caps, and effect layers—but Lantern Table's shipped shaders are original and do not require Radiant at runtime.

prefers-reduced-motion, the app's own motion/effects toggles, viewport size, and hardware concurrency all reduce the workload on lower-power/mobile devices.

Mobile and desktop

Desktop uses a three-pane tabletop layout. Narrow screens collapse to a touch-friendly bottom navigation and fixed action composer. Dice, moves, tool cards, campaign controls, timelines, themes, audio, and drawing controls remain available without requiring a desktop viewport.

Audio

Audio is opt-in per player; audio is not sent through the multiplayer server.

  • Off — default
  • System voice — browser speechSynthesis, no per-use Lantern Table cost
  • Enhanced local voice — optional Kokoro 82M loaded in the browser, using WebGPU when available and a WASM/quantized fallback otherwise

DM narration is chunked into complete sentences while text is still streaming. Creepy/ancient voice presets lower pace/pitch where supported and use local Web Audio filtering/delay for the enhanced voice path.

Kokoro is downloaded on first use rather than bundled in the container, so users who do not enable it do not pay the bandwidth/storage cost.

Multiplayer and reconnect

The host is the canonical campaign authority. Friends join with the campaign's short join code.

  • signaling: WebSocket through dnd.waylonwalker.com
  • live game data: host-star WebRTC DataChannels
  • NAT traversal: Cloudflare STUN by default
  • hard NAT/firewall fallback: optional Cloudflare TURN credentials generated by the server
  • multi-replica routing: ephemeral Valkey pub/sub
  • campaign persistence on server: none

The host must be online for canonical story progress. If the host is offline, returning players can still inspect their local replica, character, journal, timeline, maps, and attachments, but new canonical actions are blocked. Missing non-host players do not pause the table.

Browsers remember the previous role/room and try to reconnect in later sessions. A Player Pass can be exported/imported to carry a stable player identity and character draft to another device. Character ownership is tied to that player ID; players edit their own character by default while campaign-level controls remain host-owned.

Permissions

The host can control whether players may:

  • edit their own character
  • choose their portrait
  • draw/share maps
  • suggest campaign-setting changes
  • directly change campaign settings
  • control absent characters
  • allow DM control of absent characters

Timeline

Every meaningful action is represented as an event. The Timeline can show:

  • reverse chronological session events
  • actor
  • time/session
  • location
  • event type
  • per-character location/action lanes

The same append-oriented timeline supports reconnect/catch-up and gives the campaign a replayable history rather than merely a chat transcript.

Local drawing + images

The built-in pointer canvas supports pen, erase, color, size, undo, clear, and saving drawings into the local campaign asset store. Uploaded images and saved drawings are stored in IndexedDB. The DM can suggest an image prompt without sending an image-generation request.

Launch hotfix behavior

This package includes the first-production-launch fixes:

  • #join=ROOM-CODE is parsed before the campaign wizard renders, so invite links go straight into join/reconnect mode.
  • The Table sheet shows a locally generated QR code, copyable URL, room code, and native Share button when available. QR generation is in-memory; invite URLs are not stored server-side.
  • The host automatically advertises the room when campaign creation finishes.
  • The DM starts the opening scene automatically as soon as the host companion is running and ChatGPT authorization is complete.
  • Joining players never attempt localhost companion requests.
  • Signaling failures use health-aware retry/backoff instead of a tight WebSocket reconnect loop.
  • Valkey starts with persistence disabled using the image's normal entrypoint; the previous duplicated valkey-server argument has been removed.
  • Kokoro Test voice replaces/cancels the previous preview instead of queueing previews.
  • Dice/system messages render through the Markdown renderer.
  • The saved theme is applied while the page is still hidden, preventing the default-theme flash on refresh.
  • HP, max HP, AC, level, conditions, and location are game/DM-authoritative. New characters have unset mechanics until the DM establishes them. Players still own identity, backstory, appearance, and notes.
  • Effects have stronger baseline visibility, deterministic local preview overrides, and an explicit Use DM effects reset.

Invite/rejoin flow

The host opens Table and can copy or scan an invite like:

https://dnd.waylonwalker.com/#join=ABCD-123

A joining browser bypasses campaign creation, connects to signaling, receives the host snapshot, then removes the temporary join fragment from the address bar.

Public service vs local Lantern Bridge

OpenAI's current open-source Sign in with ChatGPT plan-sharing flow uses a loopback callback on the user's own device. Because of that, the Kubernetes pod is deliberately not the holder of user ChatGPT credentials.

To run the hot-reloading, single-player app locally:

just serve

Then open http://127.0.0.1:8765. The server watches Python, HTML, CSS, JavaScript, and SVG files and restarts when they change. Refresh the browser to load the updated UI. This runs Lantern Bridge in the same process, so ChatGPT sign-in and fixed loopback runtime discovery are available locally; multiplayer signaling remains disabled.

Run the public service in Kubernetes, then the campaign host runs Lantern Bridge locally:

just companion

or:

./dnd_chatgpt_server.py

The hosted page does not probe localhost from joining players. A host opens AI Connections and runs Lantern Bridge on the host device. The host may authorize ChatGPT and connect local runtimes independently. ChatGPT and Ollama DM streaming use the same normalized stream path.

Joining players never probe 127.0.0.1, never install Lantern Bridge, and never configure AI. Lantern Bridge handles host-side OAuth/token refresh and inference; the public service handles multiplayer signaling only.

Local data

  • campaign/game state: browser localStorage (lantern-table-campaign-v1:*), indexed by lantern-table-campaign-index-v1; older lantern-table-v4 saves are imported without dropping their timeline or transcript
  • game sessions: stored inside their campaign snapshot with numbered play sessions, first-class Session Zero, planned/live/paused/ended/archived lifecycle, attendance, recap, and start/end scene references
  • durable campaign/session views: #campaign=<id> and #campaign=<id>&session=<id>; opening a historical session reads its session log without restoring its old campaign state
  • uploaded images and drawings: browser IndexedDB (lantern-table-assets)
  • stable player ID: browser localStorage / optional exported Player Pass
  • ChatGPT OAuth credentials: ~/.config/lantern-table/chatgpt.json on the host Bridge only
  • local benchmark cache: ~/.config/lantern-table/benchmarks.json on the host Bridge only
  • Bridge host registration ID: ~/.config/lantern-table/host.json

Campaigns own the continuing world state. New game sessions keep that same party, location, quests, NPCs, and character state while scoping transcript and timeline context to the current session. The AI DM receives the current campaign/session identity, current session history, and bounded summaries from ended sessions. Campaign data remains on participant devices; the host browser remains authoritative for shared play.

No OpenAI API key or image-generation API path is included in this build.

The provider and progressive image-generation roadmap is documented in the Lantern Bridge handoff.

Container + homelab defaults

  • registry: registry.wayl.one
  • image: registry.wayl.one/lantern-table:<tag>
  • namespace: lantern-table
  • pull secret: existing cluster-regcred
  • replicas: 2 application pods
  • ephemeral routing: 1 Valkey pod with persistence disabled
  • ingress: Traefik
  • certificate issuer: letsencrypt-dns
  • hostname: dnd.waylonwalker.com

The manifests never create or copy cluster-regcred; they only reference the existing common pull secret.

Build and deploy

Build + push an immutable tag:

just publish

The full direct flow runs validation, builds and pushes a unique image tag, applies the manifests, waits for rollout, and runs the production doctor:

just deploy

Inspect:

just status
just rollout
just logs

The default container engine is Podman. Override with:

CONTAINER_ENGINE=docker just publish

The default tag combines the current short Git SHA (or local outside a Git checkout) with a timestamp, so repeated deploys create a fresh image. Set TAG=0.2.0 (or similar) to choose it explicitly.

Argo CD

For the real homelab path, prefer:

  1. just publish
  2. update the pinned image tag in the Argo-managed copy of k8s/deployment.yaml
  3. commit/push
  4. let Argo synchronize

The Deployment is configured with maxUnavailable: 0, maxSurge: 1, a readiness drain, and a 90-second termination grace period. Existing WebRTC games are not routed through the pod, so they continue during a signaling rollout. WebSocket signaling reconnects automatically; users normally need no restart warning.

TURN fallback

STUN works without configuration. To enable Cloudflare TURN for restrictive NAT/firewall cases, run the interactive setup:

just turn-secret

The recipe explains where to create a TURN key in the Cloudflare dashboard, waits for you to get its ID and generated token, then prompts for both (the token input is hidden). It stores them in the lantern-table-turn Kubernetes Secret, restarts the app to load them, and runs doctor. The server requests short-lived ICE server credentials and returns them to browsers. For scripted use, set CLOUDFLARE_TURN_KEY_ID and CLOUDFLARE_TURN_API_TOKEN in the environment before running the recipe.

Multi-replica signaling

The application runs two replicas. Valkey is used only for ephemeral room presence and pub/sub between signaling pods. Its manifest explicitly disables snapshots and AOF persistence, and a NetworkPolicy limits inbound Valkey traffic to Lantern Table application pods.

Campaign state, transcripts, characters, drawings, and images are never written to Valkey.

Validate locally

just validate

This checks Python compilation, browser JavaScript syntax (when Node is installed), and Kubernetes rendering (when kubectl is installed).

Third-party components

See CREDITS.md. The icon SVGs are vendored. Kokoro/kokoro-js is an optional runtime download only; its model is not included in this ZIP or container.

Hosted + localhost OAuth return

The ChatGPT OAuth callback itself remains on http://127.0.0.1:8765/auth/callback, because the open-source plan-sharing flow requires loopback on the host device. The companion now remembers the UI that launched sign-in and immediately returns there after credentials are saved:

  • launched from https://dnd.waylonwalker.com → returns to the hosted table
  • launched from http://127.0.0.1:8765 → returns to the local table

The return_to URL is allow-listed to Lantern Table origins so the companion cannot be used as an arbitrary redirector.

D&D gameplay shader pack

The atmosphere engine also supports gameplay-driven shaders and material states: wet, fire, cold, hot, acid, poison, radiant, necrotic, lightning, thunder, force, psychic, darkness, web, grease, faerie, and petrified. These are visual presentation cues only; they do not invent or apply mechanics by themselves. The DM still uses the actual rules, rolls, conditions, damage, Resistance/Immunity, and state tools separately.

The pack uses an additive WebGL canvas for full-scene energy and CSS micro-materials for buttons/cards/panels. It remains zero-dependency, pauses with page visibility, caps DPR on lower-core devices, honors reduced-motion, and leaves the existing water/underwater/aurora shader renderer intact.

Rules resolver and character memory

The browser-side rules core targets 2014 fifth edition as represented by SRD 5.1. It does not claim SRD 5.2.x / 2024 rules compatibility. Structured mechanical events carry compact rule IDs and point to the official 2014 Basic Rules in assets/rules5e.mjs. The event log is separate from DM narration, and the host's local campaign state remains canonical.

Combat uses initiative, individual surprise, movement and action tracking, current-turn IDs, basic attacks with a limited weapon set, checks, saves, reactions, and start-of-turn death saves. Freeplay remains outside initiative. Spellcasting, broad monster stat blocks, opportunity attack execution prompts, full condition coverage, rests, healing effects, and class/resource mechanics remain outside this initial slice; unsupported spell actions are rejected. Portraits and persistent NPC/PC records live in local campaign state. The Characters view shows player-visible location knowledge and entity-linked history.

The 90-second combat turn timeout, 30-second warning, automatic Dodge when legal, and optional reaction timeout decline are Lantern Table procedures, not 2014 rules. Timeout is recorded with a lantern:procedure:* source ID.

Scene continuity is stored as canonical state with stable scene and location IDs. The DM uses dedicated transition_scene and update_scene tools for arrivals and meaningful changes within a location; propose_state_patch is for narrative metadata. Scene transitions update the host immediately and appear in the timeline. Atmosphere tools affect visual presentation only. Hidden NPC destinations are kept out of player snapshots; players see their known or last-known location.

No SRD prose is copied into the implementation. Rules metadata links to the 2014 Basic Rules and SRD 5.1. If SRD 5.1 text is incorporated later, include the required CC-BY-4.0 attribution and license notice.