Developer runs real DOOM inside Claude Code's statusline
Erkan DOGAN built a system that renders the 1993 DOOM engine live inside Claude Code's statusline, using four native extension points — statusline, `UserPromptSubmit` hook, MCP server, and slash command — with no patched binaries or VS Code extensions required.
Score breakdown
Understand the limits of Claude Code's Ink-based TUI renderer — especially its cell-width miscounting with 24-bit ANSI and Unicode 13 glyphs — before building any live-updating statusline widget or terminal UI extension.
- 01The 1993 DOOM engine renders live inside Claude Code's statusline using the `doomgeneric` library.
- 02Four Claude Code extension points are used: statusline, `UserPromptSubmit` hook, MCP server, and slash command — no patched binaries or VS Code extensions.
- 03Each DOOM frame is written atomically as 24-bit ANSI to `/tmp/doom-in-claude/frame.ansi` and read by the statusline on every refresh.
Erkan DOGAN built a working DOOM-in-Claude-Code integration using only four of Claude Code's existing extension surfaces: the statusline, a `UserPromptSubmit` hook, an MCP server, and a slash command. A background daemon runs the `doomgeneric` DOOM engine and atomically writes each rendered frame as 24-bit ANSI to `/tmp/doom-in-claude/frame.ansi`. The statusline reads that file on every refresh. Keystrokes typed into the chat prompt (`w/a/s/d/f`) are intercepted by the `UserPromptSubmit` hook and forwarded as DOOM inputs; anything unrecognized passes through to Claude normally. The MCP server exposes `doom_look`, `doom_move`, and `doom_state` tools, enabling Claude itself to play. The core gameplay pipeline required a 900-line C wrapper and a small statusline shell script.
Claude Code's TUI is built on Ink — a React-for-terminals framework — which maintains a virtual-DOM-like cell grid and emits only diffs rather than full redraws.
The real challenge was rendering stability. Claude Code's TUI is built on Ink — a React-for-terminals framework — which maintains a virtual-DOM-like cell grid and emits only diffs rather than full redraws. A single DOOM frame spans roughly 3,000 cells (120 × 28), generating approximately 6,000 SGR escape sequences and around 129 KB of data per refresh. Ink's diff engine occasionally miscounts cell widths, and those miscounts compound: after roughly 30 frames, cells Ink believes are at column 42 are physically at column 43, causing colors to paint on the wrong glyphs until the image becomes noise.
DOGAN's first fix was a Perl normalizer that padded every RGB triple to exactly 3 digits, replaced 1-byte ASCII spaces with 3-byte U+2800 Braille blanks for stable byte counts, stripped DEC private mode sequences, and collapsed duplicate reset codes — achieving 97% byte-identical successive frames. The drift persisted because the normalizer only ran in the `chafa` rendering path, while the user was actually on the sextant renderer (a native C fallback). Unicode 13 sextant glyphs (`U+1FB00`–`U+1FB3B`) are not recognized by most string-width libraries, so Ink cannot measure their width, causing every sextant-heavy cell to nudge the column tracker fractionally off with each frame.
Key facts
- 01The 1993 DOOM engine renders live inside Claude Code's statusline using the `doomgeneric` library.
- 02Four Claude Code extension points are used: statusline, `UserPromptSubmit` hook, MCP server, and slash command — no patched binaries or VS Code extensions.
- 03Each DOOM frame is written atomically as 24-bit ANSI to `/tmp/doom-in-claude/frame.ansi` and read by the statusline on every refresh.
- 04An MCP server exposes `doom_look`, `doom_move`, and `doom_state` tools so Claude itself can play the game.
- 05A single DOOM frame generates ~3,000 cells, ~6,000 SGR escape sequences, and ~129 KB of data per refresh.
- 06Claude Code's Ink-based renderer drifts because it miscounts cell widths, compounding until the image becomes noise after ~30–50 frames.
- 07Unicode 13 sextant glyphs (U+1FB00–U+1FB3B) are not recognized by most string-width libraries, causing Ink's column tracker to drift when the sextant renderer is active.
Topics
Summary and scoring are generated automatically from the original article. We always link back to the publisher and never republish images or paywalled content. Last processed Apr 22, 2026 · 11:07 UTC. How this works →