CH 05

— Bulletin

◀ Return to log

Bulletin 02 · 2025-11-27

Writing a terminal that survives 40k lines/sec

A log console that keeps up with a firehose without dropping input, freezing scroll, or eating the tab's memory — and what it cost to get there.

Author
Alex Mercer
Published
2025-11-27
Reading time
2 min
Filed under
Tooling
FIG.01 — Console under a sustained 40k lines/sec soakTest rig

// Bulletin

The first version did the obvious thing: one line of log output, one appended DOM node. At a few hundred lines a second that is fine. At forty thousand it is a denial-of-service attack you wrote yourself — layout never finishes, the scrollbar becomes decorative, and the tab dies somewhere past two million nodes.

The fix is to stop pretending the DOM is the data structure. Lines land in a preallocated ring buffer that holds the last half million entries and nothing else; the DOM renders only the slice the viewport can show, about eighty rows, recycled as you scroll. Scrollback becomes an index into the ring, not a pile of nodes — memory is flat no matter how long the session runs.

Ingest and paint run on different clocks. Messages arrive whenever the socket feels like it, in bursts of hundreds; the renderer wakes once per animation frame, drains whatever is queued into the ring in one pass, and updates the visible slice once. Nothing re-renders per message. A frame that receives one line and a frame that receives nine hundred do the same amount of DOM work.

The detail that took longest was the tail — the terminal’s follow mode. Pinning to the bottom by scrolling after every append fights the user the moment they drag the scrollbar. Follow is a state, not a scroll position: on when you are at the live edge, off the instant you scroll up, back on when you return to the bottom. The ring keeps filling either way; the view just stops chasing it.

What survived the soak test was, in the end, a very old idea: fixed memory, fixed work per frame, and a strict boundary between what the machine records and what the human is looking at. The terminal does not try to show everything. It tries to never fall behind.

// In this bulletin

  • — The DOM is not a ring buffer
  • — Virtualizing scrollback
  • — Batching by animation frame, not by message
Alex MercerSolid State · Colour Television
LIGHT
  • FB
  • X
  • LI
  • GH