Skip the terminal and read the text

· runawaydevil , terminal , design

Eighty by Twenty-Five

This system is 80 columns by 25 lines. Not "about 80". Not "80 on desktop". Eighty, on a phone, on a 4K panel, in a window you have dragged to the size of a business card. What changes with your screen is how large one cell is drawn, and nothing else.

That sounds like a decoration. It is closer to a constitution: almost every awkward decision in this codebase comes from refusing to break it.

What the browser wants to do instead

Every terminal library for the web is built around the opposite idea. You give it a container, it measures the container, it reports back "you have 142 columns and 38 rows", and the program on the other end reflows to match. That is correct for a terminal that hosts someone else's program.

Here there is no other program. The system is the only thing writing to the screen, so the size can be fixed and everything above it can be composed against a known width. That is why the terminal in this site is written here rather than pulled in — not because the libraries are bad, but because their central feature is the one thing that had to go.

Nothing measures text with .length

The first real casualty was string length. "日本語".length is 3, and it occupies 6 cells. "coração" measured after Unicode normalisation can be 8 code points and 7 cells. A line with colour in it —

ESC[91mRUNAWAY ESC[0m

— has fourteen characters of escape that occupy nothing at all.

So there is exactly one module allowed to reason about horizontal space, and it counts cells: grapheme clusters, East Asian widths, combining marks, and an ANSI scanner that skips what is invisible. Everything else calls it. A test fails if any composed screen exceeds cell 80, which is how a typo gets caught before it becomes a crooked box.

Three defences, because one is not enough

  1. The model cannot exceed it. The screen allocates exactly 2000 cells. Writing past column 80 wraps. There is no code path that grows the grid.
  2. Composition measures in cells. Every listing, every header, every piece of art is measured before it ships. The art validator refuses to build a piece 92 cells wide.
  3. The renderer fits, never reflows. Playwright asserts cols === 80 and rows === 25 at 3440×1440, at 1280×720, on a tablet and on a phone.

The part that took three tries

Filling the screen is where the constraint bites. An 80×25 grid has an aspect ratio near 1.6:1; a modern monitor is closer to 1.9:1. Scale the grid until it fills the height and the glyphs come out around 38 pixels tall — legible, and absurd for reading prose. Cap the size and a wide screen shows a lot of black.

There is no arrangement that satisfies both. So the system does the honest thing: it renders at the size an 80×25 terminal has always been — 640 by 400 pixels, cells of 8 by 16 — and lets the surround be black. The frame is a hairline, so the emptiness reads as a terminal in a room rather than a page that failed to load.

It also stopped scaling with a CSS transform, which was quietly resampling every glyph. Setting the font size instead means the browser rasterises at the real size. The text got noticeably sharper the day that changed.

Why bother

Because the limit does the editing. Seventy-six usable columns is roughly the width prose wants anyway; the constraint just makes you notice. A pager forces an argument to have parts. Sixteen colours force contrast to mean something.

None of that is nostalgia. It is a set of limits that happen to produce better decisions than no limits at all.