Ninth Candle
A candlelit tabletop RPG where I wrote the rules as code, cast a language model as the narrator and nothing more, and set ComfyUI to paint each scene on my own GPU.
Solo — game design, rules engine, AI pipeline, build
- TypeScript
- ~23k lines
- Test files
- 29
- Images painted in play so far
- 221
- Commits
- 24
01 / problem
The problem
AI dungeon masters usually let the model decide everything, including whether your sword hit. That makes the game feel arbitrary, because it is. I wanted a table where the dice are honest and the model only gets to decide how the outcome sounds.
I also wanted the model to be swappable and the art to be mine: any model Ollama can reach (local weights or its cloud routing) behind one interface, and every image painted on my own GPU.
02 / build
What I built
A browser game backed by a small job server, with every rule that changes state kept in plain TypeScript.
- Rules as code. Checks roll d20 plus the sheet's own ability modifier and proficiency. HP, candles, rounds, initiative, spell slots and advancement are tracked in state. The master receives each result as a settled fact.
- A narrator behind a seam. A
DungeonMasterinterface separates resolution from narration, so a scripted narrator and an Ollama-backed one are interchangeable. - A world that remembers. Realms, places, characters with their own clocks, travel reach, a scene log, and nightly "dreaming" that turns what happened into memory.
- Jobs that outlive the tab. A Hono + SQLite server runs model calls and ComfyUI renders as jobs. A job generates against the world as it was when it started and records against the world as it is when it finishes, so nothing saved in between is lost.
- A painter with a locked anchor. Portraits, place plates and scene panels come from Qwen Image and Qwen Image Edit in one house style, with WAN turning a still into a short motion loop.
03 / signal
What it shows
Where to draw the line between deterministic code and a model: the model makes things vivid, and the code decides what is true. It also shows GPU scheduling treated as a design problem. Art renders in the gaps that reading already creates, never mid-sentence or mid-decision, and the server allows one active job per character or world.
It is a creative system that holds together as a game rather than a chat window with a theme. Right now the narrator runs on GLM-5.2 through Ollama's cloud routing, and swapping it for local weights is a setting, not a rewrite.
Gallery 1 / 8
One turn, start to finish. The 90-second paint wait is trimmed; nothing else is.