9a1c57db 2026-05-28 Pavel Safronov | 1 | # ๐ฒ Solo Dungeon Master |
| 2 | |
efa9a3e4 2026-05-28 Pavel Safronov | 3 | An AI-powered solo tabletop RPG app that runs entirely in your browser, using the **Cypher System** rules. Your API key never leaves your device โ all requests go directly from your browser to the Anthropic API. |
9a1c57db 2026-05-28 Pavel Safronov | 4 | |
b6c52441 2026-10-27 Pavel Safronov | 5 | **Live:** [apps.fuzzyworld.net/dm/](https://apps.fuzzyworld.net/dm/) |
9a1c57db 2026-05-28 Pavel Safronov | 6 | |
| 7 | --- |
| 8 | |
| 9 | ## How it works |
| 10 | |
| 11 | On your first visit you'll be asked for an [Anthropic API key](https://console.anthropic.com/). The key is saved to your browser's `localStorage` (AES-GCM encrypted) so you only enter it once. After that, the game UI appears immediately on every return visit, resuming your previous adventure automatically. |
| 12 | |
efa9a3e4 2026-05-28 Pavel Safronov | 13 | The game is powered by `claude-haiku-4-5-20251001` and uses Claude's tool-use API to maintain a structured character sheet (Might/Speed/Intellect pools, XP, cyphers, equipment) while generating freeform narrative. Costs roughly a few cents per session. |
9a1c57db 2026-05-28 Pavel Safronov | 14 | |
| 15 | ## Features |
| 16 | |
4aedf3e4 2026-05-28 Pavel Safronov | 17 | - **Cypher System character creation** โ pick a Descriptor (34 options), Type (Warrior, Adept, Explorer, Speaker), and Focus (69 options) to form the sentence *"I am a Clever Warrior who Hunts."* |
| 18 | - **Setting selector** โ choose Classic Fantasy, Ninth World (Numenera), Modern Thriller, or Science Fiction at character creation; adjusts currency, tone, special-ability vocabulary, and available Foci |
efa9a3e4 2026-05-28 Pavel Safronov | 19 | - **Three stat pools** โ Might, Speed, and Intellect each with current/max and Edge displayed; colour-coded bars shift green โ amber โ red as pools deplete |
| 20 | - **Damage track** โ one pool at 0 โ Impaired; two at 0 โ Debilitated; all at 0 โ Dead |
4aedf3e4 2026-05-28 Pavel Safronov | 21 | - **Skills** โ starting skills populated from Type and Descriptor; sidebar section shows each skill with a colour-coded rank badge (green Trained / gold Specialized / red Inability); overlapping trained skills automatically promote to Specialized |
efa9a3e4 2026-05-28 Pavel Safronov | 22 | - **Cyphers** โ one-use items found during play; displayed in the sidebar with Cypher Limit enforcement |
4aedf3e4 2026-05-28 Pavel Safronov | 23 | - **Advancement modal** โ spending 4 XP opens a choice dialog: increase a pool max by 4, add +1 Edge to a stat, train a new skill, specialize in a trained skill, or describe a custom improvement; four improvements advance your Tier (max Tier 6) |
efa9a3e4 2026-05-28 Pavel Safronov | 24 | - **Session persistence** โ adventure state and conversation history saved to `localStorage`; reloading the page resumes mid-scene; save version guard offers Markdown export before discarding incompatible sessions |
9a1c57db 2026-05-28 Pavel Safronov | 25 | - **Cost display** โ per-turn and session cost estimates shown in the sidebar (based on token counts from the SSE stream) |
| 26 | - **Dice roller** โ d4 through d100, configurable count |
efa9a3e4 2026-05-28 Pavel Safronov | 27 | - **World Notes** โ orange-bordered GM meta-context panel for providing background lore, world physics, and house rules that Claude treats as absolute truth |
9a1c57db 2026-05-28 Pavel Safronov | 28 | - **Share links** โ generate an encrypted `#k=โฆ` URL so someone else can use the app with your API key; they enter a passphrase you share separately; the key is never in the URL |
b6c52441 2026-10-27 Pavel Safronov | 29 | - **Report Issue** โ in-game button that collects debug info (character state, recent errors, session cost) and opens a pre-filled bug-report email |
9a1c57db 2026-05-28 Pavel Safronov | 30 | |
| 31 | ## Running locally |
| 32 | |
| 33 | ```bash |
| 34 | npm run dev # serves public/ on http://localhost:3000 |
| 35 | ``` |
| 36 | |
| 37 | Requires Node.js. No build step โ the entire app is a single `public/index.html`. |
| 38 | |
| 39 | ## Architecture |
| 40 | |
62945a8b 2026-05-28 Pavel Safronov | 41 | | Concern | Approach | |
| 42 | | ------------- | ---------------------------------------------------------------------------------------------- | |
| 43 | | AI backend | Anthropic Messages API, streaming SSE | |
| 44 | | Key storage | AES-GCM + PBKDF2 (Web Crypto API); falls back to XOR stream cipher on plain HTTP | |
| 45 | | Share links | AES-GCM encrypted blob in the URL hash (`#k=`); hash is never sent to the server | |
| 46 | | Session state | `localStorage` โ character sheet, conversation history, rendered HTML | |
| 47 | | Analytics | `metadata.user_id` in each API request carries `p_<playerId>:g_<gameId>` for Anthropic Console | |
b6c52441 2026-10-27 Pavel Safronov | 48 | | Deployment | apps.fuzzyworld.net โ `npm run deploy` publishes `public/` | |
9a1c57db 2026-05-28 Pavel Safronov | 49 | |
34071afe 2026-05-28 Pavel Safronov | 50 | ### Why a single file? |
| 51 | |
| 52 | `public/index.html` is currently ~3,500 lines of vanilla JS โ no build step, no bundler, no TypeScript. This is intentional. The constraint keeps the CI pipeline trivial (push `public/` โ GitLab Pages deploys it) and makes the project easy to fork and self-host without a Node.js toolchain beyond the dev server. |
| 53 | |
| 54 | The tradeoffs are real โ no type safety, no module splitting, global state everywhere โ but acceptable for a project of this size and pace. If the file grows significantly or type errors become a recurring problem, the natural upgrade path is **Vite + TypeScript**: split `index.html` into modules, add a `vite.config.ts`, update the CI `script:` step to run `vite build`, and point GitLab Pages at `dist/` instead of `public/`. That migration is well-understood and can be done incrementally. |
| 55 | |
9a1c57db 2026-05-28 Pavel Safronov | 56 | ## Reporting issues |
| 57 | |
b6c52441 2026-10-27 Pavel Safronov | 58 | Use the **๐ Report** button in the top bar โ it collects debug info automatically. |
1d2c9736 2026-05-28 Pavel Safronov | 59 | |
| 60 | ## License |
| 61 | |
| 62 | The **source code** is licensed under the [GNU Affero General Public License v3.0](LICENSE). If you run a modified version of this app as a network service, you must publish the modified source under the same license. |
| 63 | |
| 64 | The **game rules content** (Cypher System types, descriptors, foci, mechanics) is used under the [Cypher System Open License](https://www.montecookgames.com/cypher-system-open-license/) ยฉ Monte Cook Games. The CSRD is free for use in digital products; see the CSOL for full terms. |
| 65 | |
| 66 | ยฉ 2026 Pavel Safronov |
62945a8b 2026-05-28 Pavel Safronov | 67 | |
| 68 | ## About |
| 69 | |
| 70 | This game is written by Pavel asking Claude to implement features. This paragraph - written by Pavel - is the only hand-written code in this repo. Part of the experiment here is being able to create a complex RPG game with Claude, while also utilizing Claude in the game itself. Let's see how this goes. Enjoy! |