BENCHMARK: Running Doom Fire in AbsoluteTelnet/SSH

DOOM fire is the animation people reach for when they want to know how fast a terminal is. It repaints every cell on the screen, every frame, forever. There is no scrolling to optimize away and no idle region to skip — every cell changes color, every frame.

AbsoluteTelnet/SSH runs DOOM-fire-zig at about 380+ frames per second on a 180×45 grid.

What a frame costs

DOOM-fire paints two pixel rows at a time using the half-block character (U+2580, UPPER HALF BLOCK). The upper pixel row becomes the foreground color, the lower becomes the background, so one character cell carries two fire pixels. A full frame is one per cell plus the SGR sequences that color it.

The stream is 256-color indexed — ESC[38;5;nm and ESC[48;5;nm — and DOOM-fire caches the previous color, re-emitting only when it changes. Capturing the output at exactly 180×45:

Cells per frame8,100
Bytes per frame (average)138,103
Bytes per frame (peak)148,339
Bytes per cell17.05

At 400 frames per second, that is 3.24 million cells per second parsed, colored and painted, and roughly 53 MiB per second of escape sequences consumed.

The cost of a frame scales with the number of cells, so the frame rate moves with the size of the window — on a smaller grid this same build runs past 800 fps. A frame rate is only meaningful alongside the dimensions it was measured at. Ours: 180 columns, 45 rows. [TODO: hardware + connection]

Run it yourself

git clone https://github.com/const-void/DOOM-fire-zig.git
cd DOOM-fire-zig
zig build -Doptimize=ReleaseFast
./zig-out/bin/DOOM-fire

Pass -Doptimize=ReleaseFast explicitly. The build otherwise defaults to a debug build, which measures the benchmark rather than the terminal. The status line reports the frame rate as it runs.

A note on the source

We measured with two changes of our own applied, both submitted back to DOOM-fire-zig.

The first places the status line with an explicit cursor-position sequence. As written, the fire paints exactly as many full-width rows as the screen has, which leaves the cursor on the bottom-right cell with a wrap pending; the status text’s first character then resolves that wrap and scrolls the whole screen up one line, once per frame. The second makes the fire follow the terminal when you resize the window, instead of holding the size it started with.

Neither changes what the terminal has to parse. Measured at 180×45, our build emits 138,103 bytes per frame against the original’s 137,680 — a difference of 0.3%.

DOOM-fire-zig is const-void’s, and it is a genuinely good piece of work — a benchmark that is fun to watch is a benchmark people actually run.

Measured on AbsoluteTelnet/SSH 14.21.

Leave a Comment

Your email address will not be published. Required fields are marked *