Turn the tokens you spend on LLMs into EXP. A token-driven 16-bit pixel RPG that levels up while you work.
Every token you burn in Claude Code (or any local LLM) becomes EXP and combat energy for a hero descending an endless dungeon. You work normally; the hero levels, auto-battles randomized monsters, and drops loot across six rarities. Glance back after a real working session and there's a stack of loot and level-ups waiting — the dopamine hit.
Token usage drives progress — not the clock. This is not an idle/AFK game. Nothing happens while you sit still. Spend tokens and the world moves; stop and it freezes. "Check back every 15–30 minutes" works because that's a window of real usage replayed as a "While you were away…" reveal.
npm install
npm run build # build the web UI
npm start # serve game + API on http://localhost:8787Open http://localhost:8787 and start feeding it tokens.
For development with hot reload (UI on :5173, server on :8787):
npm run devNo LLM handy? Use the Feed tokens controls in the dashboard, or
curl -X POST localhost:8787/api/ingest -d '{"tokens":120000}'. The whole game is playable with zero LLM connected.
Just run the server. It automatically watches your Claude Code session transcripts at ~/.claude/projects/**/*.jsonl across all your projects and feeds the token usage to a single global hero.
- Starts fresh: existing history is baselined and ignored on first launch — you begin at Level 1.
- Never misses tokens: per-file byte offsets persist, so restarting catches up on exactly what you used while it was off, with no double-counting.
- Robust: usage is parsed defensively (located by its
*_tokenskeys), so a Claude Code transcript format change won't silently break capture.
Leave it running in the background, code as usual, and check back when you want the reveal.
Run the server with an OpenAI-compatible upstream, then point your tool at the proxy:
TOKEN_FABLE_PROXY_UPSTREAM=http://localhost:11434 npm start
# then set your tool's base URL to: http://localhost:8787/proxy/v1The proxy streams bytes straight through and only reads the token counts as they pass (OpenAI usage, Ollama eval_count/prompt_eval_count), so overhead is negligible.
POST /api/ingest with either a shorthand or a full breakdown:
curl -X POST localhost:8787/api/ingest -d '{"tokens": 5000}'
curl -X POST localhost:8787/api/ingest -d '{"input":2000,"output":4000,"cacheRead":80000,"cacheCreation":1000}'- EXP & leveling — weighted tokens grant EXP (× your gear's EXP multiplier). Infinite curve: levels fly early, then stretch out.
- Token weighting — token types are valued by real cost/effort:
output(×4.5) >input(×1) >cache-creation(×1.25) >cache-read(×0.1). - Model tiers — a token from a costlier model is worth more EXP:
opus(×3) >sonnet(×1.5) >haiku(×0.8); local/unknown models are ×1. So an Opus session is a bigger adventure (and you can't cheaply grind on a tiny model). Tiers live insrc/engine/balance.ts. - Endless dungeon — tokens are spent as combat energy auto-battling procedurally generated monsters; deeper floors mean tougher foes and better loot. Under-geared? You stall (never die) until more levels or gear break you through.
- Loot & rarity —
common → uncommon → rare → unique → legendary → mythical. Luck biases drops toward rarer tiers. - Gear matters twice — items boost combat (ATK/DEF/HP) and meta progression (EXP multiplier, Luck), a self-reinforcing loop.
- Essence economy — salvage unwanted gear into Essence, then spend it to upgrade an item, reroll its stats, or expand your bag. Set an auto-salvage rule to scrap low tiers hands-free.
- The reveal — quiet while you're heads-down; loud when you return. A summary card + a highlights reel of your rare+ finds.
All optional, via environment variables:
| Variable | Default | Purpose |
|---|---|---|
PORT |
8787 |
Server port. |
TOKEN_FABLE_DATA |
./data |
Save directory (save.json). |
TOKEN_FABLE_PROXY_UPSTREAM |
– | Enable the local-LLM proxy (e.g. http://localhost:11434). |
TOKEN_FABLE_CLAUDE_DIR |
~/.claude/projects |
Claude Code transcripts root. |
TOKEN_FABLE_SEED |
fixed | RNG seed for a fresh save. |
Game balance (curves, drop tables, costs, scaling) lives in one file: src/engine/balance.ts.
A pure, deterministic engine wrapped by a thin server and a Preact + canvas UI.
Claude Code .jsonl ─┐
local LLM (proxy) ─┼─▶ tokenEvent ─▶ engine.advance(state, event) ─▶ events
manual /api/ingest ─┘ │ (pure · seeded · tested)
▼
persist (JSON) + WebSocket ─▶ Preact + canvas dashboard
src/engine/— pure game logic.advance(state, tokenEvent) → { state, events }. No I/O, seeded RNG, fully unit-tested.src/server/—node:http+ws. Capture adapters, the driver, atomic persistence, live push.src/web/— Preact UI + a canvas combat stage (sprite duel, HP bars, damage numbers).
npm test # vitest — engine unit tests
npm run typecheck # tsc --noEmit
npm run dev # hot-reload server + UI
npm run build # production web buildFinal hand-crafted pixel sprites · audio (chiptune SFX) · class/build system · multiple themed dungeons & bosses · data-driven JSON config + content packs · OpenTelemetry capture · multi-machine sync · achievements · packaged distribution (npx).
The current sprites are simple procedural placeholders — distinctive 16-bit art is a planned asset pass.
MIT
