Skip to content
← All work
2026In progressRuns locally

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 DungeonMaster interface 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.