Skip to content

browsh

Batch 8 (readers / browsers / hierarchical-navigation TUIs).

browsh renders real, fully-rendered web pages — including images, video, and CSS layout — into a terminal, by driving headless Firefox and converting its rendered output into terminal cells. The architecture is two parts: a Firefox webextension that runs inside the browser and extracts both the page’s text (with positions) and a downscaled pixel framebuffer, and a Go terminal client that receives that data and paints it to the TTY. Graphical regions are drawn with the unicode half-block character, encoding two vertically-stacked pixels in a single character cell; text regions are drawn as actual glyphs layered on top. Most keys and mouse gestures behave like a normal browser.

  • Half-block pixel rendering is the headline technique — the single strongest single-color rendering takeaway in this batch (see “Single-color adaptation”). It lets a text grid behave like a 2×-vertical-resolution pixel display.
  • Browser-style UI chrome on a text grid. browsh reproduces the affordances people expect from a browser — a content viewport with a chrome strip (URL/address area, status) framing it. For KN-86 this validates the Row-0 status / Rows-1–23 content / Row-24 action layout (per Canonical Hardware Specification) as a legitimate “chrome around a viewport” pattern, and is a reference for any cart presenting a navigable document viewport with a persistent control strip.
  • Dual text + graphics layers. browsh’s key architectural insight is keeping text as real glyphs and only the graphical remainder as half-block pixels, compositing them. KN-86’s SPLIT mode is exactly this: a TEXT plane and a BITMAP plane that coexist. browsh is the working proof that “crisp glyphs where there’s text, pixel-encoding where there’s imagery” is the right division — never pixel-encode text you could render as a glyph.
  • Downscale-then-encode pipeline. browsh has Firefox downscale the page to terminal-cell resolution before shipping pixels, so the client just maps cells 1:1. KN-86’s image path should mirror this: scale source imagery to the target cell count first, then encode — don’t encode at full resolution and downscale glyphs.
  • Client/renderer split. The thin terminal client + heavy off-device renderer split echoes KN-86’s own Pi (orchestrator/renderer) ↔ Pico coprocessor split and the emulator ↔ device split — a reference for keeping the display client dumb and the content producer authoritative.

This is the entry’s payload. browsh’s half-block technique is already monochrome-ready — it’s a brightness/position trick that happens to carry color, and strips cleanly to single-color amber.

The half-block mechanism (from browsh’s own source): browsh uses the lower half-block (U+2584). One character cell then carries two stacked pixels: the top pixel is the cell’s background color, the bottom pixel is the cell’s foreground color (the color of the glyph itself). The renderer walks the pixel framebuffer two rows at a time (y += 2), reading the upper row as the background and the lower row as the foreground for each cell. Result: a character grid renders at 2× its vertical pixel resolution.

KN-86 amber-on-black adaptation: KN-86 is single-color, so “color per pixel” becomes “on/off per pixel” — or, with the amber-brightness ramp KN-86 already commits to, a small set of intensity levels. Each cell encodes two vertical sub-pixels:

Top pixelBottom pixelCell
offoffspace (blank)
onoff (upper half-block)
offon (lower half-block)
onon (full block)

That four-glyph set ( ▀▄█) gives a pure-monochrome 2×-vertical-resolution pixel canvas with no color dependency at all — the position of the lit half is the pixel. Layering KN-86’s amber-intensity ramp on the fg/bg gives graded brightness per half-cell without leaving the monochrome palette. This is the canonical KN-86 pixel-from-glyph primitive, and it matches the -halfblock technique already documented at asciinator and in the ascii-effects.md spec — browsh is the browser-scale proof that it works for full-page imagery, not just small effects.

  • Go + headless Firefox is not a KN-86 dependency path — the Pi Zero 2 W can’t host headless Firefox, and KN-86 has no live web to render. The value is purely the half-block rendering technique and the text/graphics compositing discipline.
  • Cross-link asciinator (Batch 3) — the half-block- technique with fg+bg, three render modes; browsh is the same primitive proven at browser scale.
  • Cross-link ascii-effects.md — the KN-86 character-rendering spec the half-block path feeds.
  • Cross-link mapscii — the braille-density sibling: half-block gives 2× vertical resolution and clean filled regions; braille gives 2×4 sub-cell resolution for fine sparse detail. KN-86 wants both in the toolkit, chosen by content type.
  • Cross-link w3m — the true-overlay alternative to character-encoding imagery; browsh and w3m bracket the two ways to put a picture in a text UI.