# The open-source landscape for terminal, cyberpunk and minimal web UI
A survey written while building [`examples/`](examples/) — fifty-nine runnable
specimens, each driving a real library rather than a reimplementation of one.
Every verdict below comes from putting the thing on screen, not from reading
its README.
**Method.** Candidates were found by search, then filtered on three things:
licence, last activity, and whether the project is *usable as it ships* rather
than as a starting point to fork. Each survivor was vendored at a pinned
version (`scripts/vendor.sh`), driven from a specimen, and judged on what it
actually did. Sizes below are the **vendored file, minified, not gzipped** —
measured, not quoted from marketing copy.
Dated **2026-08-31**, extended **2026-09-01** twice: first with the React strand,
the era-GUI strand, sound, instrumentation, and two measured passes over the
whole lab — [`audit.html`](audit.html) for accessibility and
[`profile.html`](profile.html) for performance under load; then with the four
specimens that close the two thinnest parts of the survey. Terminal gained the
things that render an *actual* terminal (§1) rather than themes that look like
one, and cyberpunk gained the register the rest of §2 was missing — machine
vision and codec corruption, both carried by content that means something
rather than by frame geometry.
Extended again **2026-09-02**, and this pass mostly corrected the survey rather
than adding to it. A new **interaction strand** (§9) gives the lab three
specimens you operate instead of one, and two more measured passes went over
everything — [`mobile.html`](mobile.html) at 390×844 under touch emulation, and
[`announce.html`](announce.html) for what a screen reader would actually say.
Between them they overturned a claim this file had been making since the first
pass: the contrast failures that survived the high-contrast switch were **not**
greys inside vendored sheets. Seventy-six of eighty-three were the lab's own
specimens hard-coding a hex, and the reason nobody knew is that the audit page
could count failures but never said whose colour they were.
[`contrast.html`](contrast.html) is what asks.
Extended once more **2026-09-02**, and this is the pass that went after the
lab's blind spots rather than its gaps. Every specimen up to this point was
dark, English, left-to-right, monospaced, rectilinear, and working — six
assumptions so uniform nobody had noticed they were assumptions. Ten specimens
break them, and two more strands hold the ones that did not fit anywhere:
**data surfaces** (§10) for a table, a map and a waveform, and **typography**
(§11) for the two specimens about the lab's own voice. The single most useful
thing to come out of it is §12: a light control that shows how much of the house
grammar was technique and how much was just the field being black. That control
was removed on 2026-09-04 and §12 is now the record of what it found.
Extended **2026-09-03**, and this pass added no libraries at all. It **split the
era strand** (§6), which had been one specimen carrying five findings, into one
per sheet plus the comparison — and the interesting part is that every one of
the three claims §6 used to make was written from reading the sheets rather than
counting them, and two of the three were wrong. Findings 85–92 are what the
counting produced. Nothing was vendored, nothing was upgraded, and the section
is longer than it was.
Extended **2026-09-04**, and this pass **took the light out of the lab**. Eight
surfaces were light — 28-bench and 38-ops-floor by design, and the six era-GUI
specimens by quotation — and all eight are now dark. This is the one kind of
pass that cannot add a finding by looking harder, because nothing was measured
that had not been measured before; what it did instead was CHANGE ONE VARIABLE
UNDER EVERY EXISTING MEASUREMENT AND SEE WHICH ONES MOVED. Most of §12 reversed,
which is what §12 predicted. Two numbers in §13 that were expected to move did
not move at all, which is finding 93 and the better result. And the era strand
turned out to have been resting on something nobody had written down: **not one
of the five sheets declares a single custom property**, across 683 kB, so there
was no token to set and the palette had to be substituted by walking the parsed
stylesheet. Findings 93–101, and `lab/relight.js` is the machinery.
Stars and activity are from the GitHub API on those dates; they decay, the
verdicts mostly do not.
---
## 1 · Terminal / TUI
The monospace-grid register. The finding that governs the whole strand is
specimen 04: **most of the "hacker UI" read is carried by the character grid
itself**, before any library. A 5-row block-character bar chart with no
dependency at all holds its own against everything else here.
| project | ver | licence | ★ | last push | size | verdict |
|---|---|---|---|---|---|---|
| [WebTUI](https://github.com/webtui/webtui) | 0.1.10 | MIT | 2.4k | 2026-08 | 39 kB | **The strongest option.** Pure CSS, `@layer`-based so it composes instead of fighting you, and the box borders are real box-drawing characters rather than 1px rules faked to look like them. Attribute API (`box-="double"`, `is-="badge"`). Costs a hard commitment to a monospace grid. |
| [TuiCss](https://github.com/vinibiavatti1/TuiCss) | 2.1.2 | MIT | 2.0k | 2026-06 | 34 kB | **The most complete period reconstruction.** Real menus, tabs, modals, tables, charts, statusbars, the full 16-colour DOS palette as utility classes. Also the most opinionated: you adopt the whole 1990 look. Re-themes cleanly to dark using its own palette classes. |
| [terminal.css](https://github.com/Gioni06/terminal.css) | 0.7.5 | MIT | 1.4k | 2026-02 | 15 kB | **Not actually a TUI library** — a small, tasteful *document* theme that happens to be green-on-black, with a blinking prompt cursor built in. Right for a blog or a docs page; wrong for an interface. |
| [hack.css](https://github.com/egoist/hack) | — | MIT | — | — | — | Not vendored. Overlaps terminal.css and is less maintained; nothing it does is unavailable above. |
**And the other half of the strand: things that render an actual terminal.**
Everything above is a *theme*. These four are not, and specimens 21 and 23 run
them against one real recorded session and one real prompt.
| project | ver | licence | size | verdict |
|---|---|---|---|---|
| [xterm.js](https://github.com/xtermjs/xterm.js) | 5.5.0 | MIT | 289 kB | **A real terminal emulator** — the one to reach for when it must accept input: a shell, a serial console, a container exec. It knows nothing about playback, so any replay scheduler is yours to write. |
| [asciinema-player](https://github.com/asciinema/asciinema-player) | 3.8.1 | Apache-2.0 | 187 kB | **For showing a recorded session, and nothing else.** You get the clock, the scrubber and the controls; you give up control of the rendering. |
| [ansi_up](https://github.com/drudru/ansi_up) | 6.0.2 | MIT | 23 kB | **A converter, not an emulator**, and the distinction is the whole finding. Beautiful ANSI-colour-to-HTML in 23 kB, no cursor concept at all — so it renders coloured logs perfectly and cannot render a full-screen TUI in any form. Ships ESM, not UMD. |
| [jQuery Terminal](https://github.com/jcubic/jquery.terminal) | 2.44.1 | MIT | 176 kB | **A command interpreter with a REPL attached** — history, tab completion, argument parsing, its own colour markup. Not an emulator either, so no cursor addressing. The real cost is jQuery: 88 kB before you start. |
**The line that matters is cursor addressing.** A program that only *appends*
can be rendered by anything. A program that *moves the cursor* — which is every
full-screen TUI there is — needs an emulator. That single question sorts this
whole table, and specimen 21 makes it visible by feeding one recorded vim
session to all three renderers at once.
**Also relevant, not used:** [xterm.js](https://github.com/xtermjs/xterm.js) is
the real answer when you need an *actual* terminal (a live shell, an SSH
session) rather than the look of one. It is a different tool for a different
job and would have made a dishonest specimen here.
---
**Added in the seventh pass: 53-mixed-script**, which takes §11's non-Latin
findings onto the character grid, where they get much worse. A terminal is a
fixed matrix of cells and every rule it has about how many cells a character
occupies is from 1991: wcwidth 1, wcwidth 2 (which only works if the font makes
those glyphs exactly twice the Latin advance, and it does not), wcwidth 0 for
combining marks — which means Thai, stacking up to three marks on one base, has
no correct representation in the model at all. Not a bug in any terminal; a gap
in the model. Finding 82, and the wider version of it: **the moment an interface
draws a box out of characters it has made a promise it can only keep for one
script**, which specimens 01, 02, 03, 21 and 23 all make.
---
## 2 · HUD / FUI
Renamed in the sixth pass. This section used to be called "Cyberpunk / HUD / FUI" and the first word was doing no work: every specimen in it is an instrument that performs competence. The actual cyberpunk register — street level, multi-script, worn, oversold — is §14, and 24-image-decay moved there.
| project | ver | licence | ★ | last push | size | verdict |
|---|---|---|---|---|---|---|
| [augmented-ui](https://github.com/propjockey/augmented-ui) | 2.0.0 | BSD-2 | 1.4k | 2024-05 | 163 kB | **Does one thing and is genuinely the best tool for it**: clipped, notched, scissored panel geometry with a border drawn along the polygon. All geometry, zero opinions about type or colour, so it layers over anything. Its corner sizes are custom properties, so with `@property` the *geometry itself* is animatable. 163 kB is the price of every corner permutation — subset it in production. Quiet for two years, but it is finished, not abandoned. |
| [Arwes](https://github.com/arwes/arwes) | 1.0.0-next | MIT | 7.6k | 2026-07 | 56 kB (95 kB with deps) | **The most serious attempt at a real sci-fi design system**, and the only project here that treats *sequencing* as a first-class primitive. Usually presented as React, but `@arwes/frames` + `@arwes/animator` are framework-agnostic and drive fine from vanilla JS — specimen 06 uses eight frame presets with no React at all. Still alpha, the API moves, and it hands you primitives to build a design system rather than components to use. |
| [LCARS](https://github.com/joernweissenborn/lcars) | main | MIT | 371 | 2021-01 | 103 kB | **Worth studying as the one FUI language that became a real design system.** Strict colour roles, a fixed radius grammar, rigid alignment on a 0.25 rem gutter. Ships the structural vocabulary (`lcars-row`/`column`/`bar`/`elbow`) plus the full Okuda palette. Unmaintained since 2021 and it does not matter — the spec it implements stopped changing in 1994. |
**Reference, not code.** The genre's actual source material is film and game
work: [HUDS+GUIS](https://www.hudsandguis.com/) is the standing archive, and
Territory Studio (*Blade Runner 2049*, *Guardians of the Galaxy*) is the
reference studio. Two things transfer directly and are visible in specimens
05–08: **numbers that nobody is meant to read** signal a machine thinking
rather than data, and **sequencing beats styling** — the four-beat
sweep → contact → lock → readout carries more than any amount of panel chrome.
---
**Two libraries that are not FUI libraries at all, and carry the strand's two
strongest specimens.**
| project | ver | licence | size | verdict |
|---|---|---|---|---|
| [mos6502](https://www.npmjs.com/package/mos6502) | 1.1.1 | MIT | 26 kB | A real MOS 6502 CPU emulator with a real instruction decoder. Specimen 22 runs actual machine code through it so the T-800's scrolling assembly is genuinely executing — which is historically right, since the film used real 6502 listings. ⚠ Its `debug` stream desynchronises from execution (finding 27); use `decode()` against `getState().pc` instead. |
| [glitch-canvas](https://github.com/snorpey/glitch-canvas) | 1.1.12 | MIT | 14 kB | **The only honest glitch library here.** It corrupts bytes inside the compressed JPEG and lets a real decoder produce the artefacts, rather than drawing a picture of damage. Specimen 24 puts it beside the usual CSS slice-and-offset and the two diverge in *kind*, not degree. |
**Why the strand needed them.** The rest of §2 is clean sci-fi console — LCARS,
Arwes' polished frames, notched panels. That is one register of this aesthetic
and it is the politest one. Machine vision and codec corruption are the other:
dirty, red, broken, and carried by content that means something rather than by
frame geometry.
---
## 3 · Minimal / classless
Specimen 09 puts byte-identical semantic HTML under all three. Demo pages are
always cherry-picked to flatter their own framework; the same markup is the
only fair test.
| project | ver | licence | size | verdict |
|---|---|---|---|---|
| [Pico](https://github.com/picocss/pico) | 2.1.1 | MIT | 81 kB | **The most capable.** Real components from plain semantic HTML — dialogs, accordions, switches, dropdowns — plus a proper dark theme. The one to pick if the page has any interface in it. |
| [Simple.css](https://github.com/kevquirk/simple.css) | 0.1.3 | MIT | 19 kB | **The most opinionated typographically**, and the most "traditional web" of the three. Auto dark theme from `prefers-color-scheme`. |
| [Water.css](https://github.com/kognise/water.css) | 2.1.1 | MIT | 22 kB | **The smallest and quietest.** Auto light/dark, CSS-variable customisation, no components. Right for a README page or a prototype. |
**The strand's real finding is specimen 10, and it is not about any of them.**
Most of what teams still pull an animation library in for is now native,
composited CSS: `animation-timeline: view()` for scroll-driven reveals with no
observer and no rAF; `linear()` for genuine sampled spring easing; and
`@starting-style` + `transition-behavior: allow-discrete` for animating in from
`display: none`. Specimen 10 runs zero frames of JavaScript animation and
reports live whether the browser has each feature. **Reach for a library when
you need sequencing and coordination — not to fade something in on scroll.**
---
## 4 · Motion engines, shaders and effects
### Engines
Specimen 11 authors the *identical* staggered grid animation three times so the
only variable is the engine.
| project | ver | licence | size | verdict |
|---|---|---|---|---|
| [GSAP](https://github.com/greensock/GSAP) | 3.15.0 | MIT | 71 kB | **Still wins for choreography, and is now fully free including every plugin.** The argument is not that it animates — it is that a twelve-step sequence becomes a single labelled object you can scrub, seek and reverse. Specimen 08 exposes exactly that: a live scrubber over a labelled timeline, with every readout derived from `tl.time()`. Nothing else here makes the sequence a value you can hold. |
| [Motion](https://github.com/motiondivision/motion) | 13.1.1 | MIT | 136 kB | The React-ecosystem default, with a vanilla API. Its springs are real physics, so `duration` is a hint and settle time is emergent — the one visible difference between the three lanes, and a philosophical choice rather than a bug. Heaviest of the three. |
| [anime.js](https://github.com/juliangarnier/anime) | 4.5.0 | MIT | 115 kB | v4 is a full rewrite with named ESM exports; the UMD build hangs them off an `anime` global rather than exposing one callable function as v3 did. Notably it now ships **built-in text utilities** (`splitText`, `scrambleText`), which overlap the text-effect libraries below. |
Read the code panels in specimen 11, not the motion: the three look nearly
identical on screen. **Choose on authoring ergonomics and bundle cost.**
### Text effects
| project | ver | licence | size | verdict |
|---|---|---|---|---|
| [Splitting.js](https://github.com/shshaw/Splitting) | 1.1.0 | MIT | 4 kB | **Punches far above 4 kB.** Shards text into per-character spans and stamps `--char-index` on each. That index is the entire API; every effect is then one CSS rule using it as a negative delay. |
| [Glitched Writer](https://github.com/thetarnav/glitched-writer) | 2.0.29 | MIT | 22 kB | A per-character state machine for scramble/decode. Unmaintained since 2021 but self-contained and it works. Note anime.js v4's `scrambleText` now covers the same ground if you already have anime. |
Scramble-decode is **the most overused effect in this aesthetic and the easiest
to do badly**. Specimen 12 puts four techniques side by side; the one that
survives being used seriously is block redaction, not scramble.
### Shaders
| project | ver | licence | size | verdict |
|---|---|---|---|---|
| [OGL](https://github.com/oframe/ogl) | 1.0.11 | MIT | 130 kB | **The right size for post-processing work.** three.js does everything; OGL does WebGL with the boilerplate removed and nothing else, and you keep writing actual GLSL. Its `Post` helper handles render-target bookkeeping in three lines. 130 kB against three.js' 635 kB. |
| [@paper-design/shaders](https://github.com/paper-design/shaders) | 0.0.80 | Apache-2.0 | 199 kB | **The best-looking shader backgrounds available off the shelf**, and the defaults are tasteful. ~30 shaders exported as plain GLSL strings — that GLSL is the real asset and it is reusable on its own. See the caveat in Findings below before using the vanilla path. |
| [three.js](https://github.com/mrdoob/three.js) | 0.185.1 | MIT | 635 kB | Vendored for comparison only. Correct when you need a scene graph, loaders and materials; overkill when you need one fullscreen quad. |
**Nothing beat raw WebGL2 for specimen 13.** A CRT pass is one fullscreen
triangle and one fragment shader; a library adds abstraction over roughly forty
lines of setup and hides the mechanism that is the whole point.
---
## 5 · React component libraries
The four libraries most of this aesthetic actually ships in, and the strand the
first pass deliberately skipped. None of them is an npm package you import:
all four are **copy-paste** libraries, so what you adopt is their source, and
their source is therefore what specimen 19 runs — fetched from their real
distribution points, compiled in the browser, unmodified.
The governing finding is that **there is no engine comparison to make here.**
All four import from `motion/react`. Where the vanilla strand has GSAP, Motion
and anime.js genuinely competing (specimen 11), this strand settled that
question already. What is left to choose on is licence, packaging and taste.
| project | distribution | licence | verdict |
|---|---|---|---|
| [Magic UI](https://magicui.design/) | shadcn `registry-item.json` over HTTP | MIT | **The most professional packaging.** A real machine-readable registry, so `shadcn add` works and so does a script. Largest catalogue. The components are the most "finished" — closer to set pieces than to primitives. |
| [Aceternity UI](https://ui.aceternity.com/) | shadcn registry, undocumented | **unstated** | **The most cinematic defaults, and the licence problem.** Its registry documents carry no SPDX field and the site publishes no licence. Treat as source-available for study; do not assume MIT before asking. |
| [React Bits](https://reactbits.dev/) | raw files in the repo | **MIT + Commons Clause** | **The only one that works without Tailwind** — it ships a plain `.css` beside each component. The Commons Clause forbids *selling the software itself*, which is fine inside a product and fatal if you are building a component library on top of it. Read it before adopting. |
| [motion-primitives](https://motion-primitives.com/) | raw files in the repo | MIT | **The smallest and most restrained.** Primitives to build with rather than finished set pieces. If you want a vocabulary rather than a catalogue, this is the one. |
**The toolchain is the cost.** Running these honestly needs React, ReactDOM,
Babel, Tailwind and Motion — ~3.5 MB before a single component renders.
A real project pays that once at build time. It is still the reason the rest of
this lab is vanilla: every other strand's specimen is the library and nothing
else.
---
## 6 · Era GUI
Faithful reconstructions of shipped operating systems. Nostalgia, but with real
interaction rules underneath, and one properly useful finding about motion.
| project | ver | licence | size | verdict |
|---|---|---|---|---|
| [98.css](https://github.com/jdan/98.css) | 0.1.20 | MIT | 24 kB | **The reference implementation of the genre.** Pixel-exact, tiny, and its class vocabulary became the de-facto standard the other two follow. |
| [XP.css](https://github.com/botoxparty/XP.css) | 0.2.6 | MIT | 256 kB | Same vocabulary, later era. **91% of the file is 53 embedded SVGs** — pictures of its own chrome, measured in 56. |
| [7.css](https://github.com/khang-nd/7.css) | 0.16.0 | MIT | 93 kB | Same vocabulary again, plus the only real motion here — 19 transitions and a pulsing focus ring. Its four opt-in classes are **Aero capabilities, not motion classes**; see finding 88. |
| [system.css](https://github.com/sakofchit/system.css) | 0.1.11 | MIT | 20 kB | Mac System 6, and the smallest sheet here. Same *idea*, **different contract** — `.standard-dialog`, `.window-pane`, `.title`. Not drop-in with the Windows three, and it declares three of their names anyway. |
| [NES.css](https://github.com/nostalgic-css/NES.css) | 2.3.0 | MIT | 289 kB | No window primitive at all, because a console had none. **Bigger than XP.css and only 3% imagery** — it draws 14,888 box-shadow pixels instead. Beautiful, and the least reusable thing here. |
**Split into six specimens on 2026-09-03, and down to five on 2026-09-04.**
This strand was one file for a month — 18 rendered a dialog under all five
sheets and carried five findings at once. Each sheet now has its own specimen.
18 itself was **withdrawn** a day later, and the reason is layout rather than
subject. Five panes side by side is the entire argument, because the swap test
is a *comparison* and a comparison you have to scroll between is not one — and
five panes do not fit. The stage is `repeat(auto-fit, minmax(250px, 1fr))`
beside a rail, so below about 1650 px the fifth wraps onto a row of its own and
the sheet you are meant to be comparing against is the one you cannot see. Two
of the five also overflow their cells at that width: system.css's dialog
measures 320×256 in a 301×461 frame and is clipped on the right, which turns a
real finding about that sheet into something that reads as a rendering bug.
Splitting the strand is what made it expendable. The five findings it used to
carry are carried by 55–59 with room to measure properly, and the one question
it kept — which sheets are actually interchangeable — is a five-row table rather
than five screens. **That table is the second one below, and it reads better
here.** The specimen, its page and its findings are untouched in the repository;
`hidden: true` in the catalog is the only thing keeping it off the contact
sheet, and deleting that line puts it back.
| specimen | sheet | the finding |
|---|---|---|
| [55](examples/55-bevel/) | 98.css | the whole OS is four colours in four slots |
| [56](examples/56-luna/) | XP.css | 91% of the file is pictures of its own chrome |
| [57](examples/57-aero/) | 7.css | the motion, and the one kind no switch can stop |
| [58](examples/58-onebit/) | system.css | three values a one-bit screen did not have |
| [59](examples/59-cartridge/) | NES.css | 15,000 pixels drawn in the cascade |
### Which of them are actually interchangeable
Specimen 18's finding, kept here now that the specimen is not on the sheet. One
dialog, nine classes — `field-row`, `field-row-stacked`, `status-bar`,
`status-bar-field`, `title-bar`, `title-bar-controls`, `title-bar-text`,
`window`, `window-body` — and the count is each sheet's own class vocabulary
taken off its parsed selectors, not how many elements it happens to style. That
distinction is the whole measurement: counting matched elements scores all five
at or near 100%, because every one of them styles `body` and most reset with
`*`, and the page would cheerfully report that NES.css renders a Windows dialog
perfectly.
| sheet | of nine | verdict |
|---|---|---|
| 98.css | **9** | drop-in. The reference vocabulary; the other two follow its names. |
| XP.css | **9** | drop-in, following 98.css. |
| 7.css | **9** | drop-in, following 98.css. |
| system.css | **3** | **not** drop-in, and the most misleading number here. |
| NES.css | **0** | no window primitive at all. Its pane is close to bare HTML. |
**The interesting number is system.css's 3, not the trio's 9.** It declares
`.window`, `.title-bar` and `.field-row` and means something else by all three,
so it *answers* a third of the markup rather than ignoring it — a title bar of
its own design wrapped around a body it has no rule for, since `.window-body`,
`.title-bar-text`, `.title-bar-controls` and both status-bar classes are absent.
NES.css scores zero and looks obviously wrong; system.css looks half-built, and
**half-built is what ships.** Partial name collision is worse than none, because
none of it announces itself.
**All six went dark on 2026-09-04**, with the rest of the lab — 18 included,
on the same day it was withdrawn for the layout reasons above. That is the pass
that produced findings 94–101, and the reason it produced so many is that
turning a faithful reconstruction dark is the one operation its authors never
had to consider — Windows 98, XP, 7 and System 6 did not ship a dark mode, so
nothing in any of these sheets was built to survive one. `lab/relight.js` is the
shared machinery: it walks each parsed stylesheet and emits an override, because
**none of the five declares a custom property** (finding 94) and there is
therefore nothing to set. Nothing in `vendor/` is edited and every count on
every one of these pages is still read off the sheet as vendored, so the
measurements are unaffected by the repaint.
The split also **corrected three things this section used to say**, which is the
argument for splitting rather than the cost of it. Each of the three was written
from reading rather than from counting.
- **"98.css, XP.css and 7.css accept byte-identical markup" was true and
unquantified.** 18 now measures it as a class-vocabulary overlap and the
answer is 9, 9, 9, 3, 0 against the dialog's nine classes. The interesting
number is system.css's 3, not the trio's 9: it declares `.window`,
`.title-bar` and `.field-row` and means something else by all three, so it
*answers* a third of the markup rather than ignoring it. **Partial name
collision is worse than none** — NES.css scores zero and looks obviously
wrong, while system.css looks half-built, and half-built is what ships.
- **"7.css is the only sheet shipping motion classes: `.animate`, `.glass`,
`.has-shadow`, `.can-hover`" was wrong.** Read in the sheet, only `.animate`
is motion; the others are translucency, depth and hover behaviour. What they
share is that **every one is an Aero capability**, and Windows 7 made each
conditional at runtime — a machine without a WDDM driver dropped to Aero
Basic and lost the glass, the shadows, the animations and the hover previews
together. The sheet reproduces a *capability negotiation*, which is a better
thing than the note credited it with.
- **"The motion vocabulary dates the compositor: none, then decoration, then
structure" was a ramp, and the counts are not a ramp.** 57 measures
0/0/0 for both 98.css and system.css, **two animations for NES.css and both
are `steps(1)`**, exactly one `@keyframes` for XP.css — the indeterminate
progress bar, the single animation an OS cannot do without — and 19
transitions for 7.css. Four sheets, four different reasons, and only the last
is about taste.
---
## 7 · Sound
Almost absent from this space, and the one channel a screenshot hides
completely. Three libraries, three different jobs, and they are not competitors.
| project | ver | licence | size | verdict |
|---|---|---|---|---|
| [ZzFX](https://github.com/KilledByAPixel/ZzFX) | 1.3.2 | MIT | 10 kB | **The right default for interface sound.** A sound is a number array; there are no assets to host, cache or version. `ZzFXMicro` is 1 kB. |
| [@arwes/bleeps](https://github.com/arwes/arwes) | 1.0.0-next | MIT | 5 kB | **The management model is the product, not the sounds.** Named bleeps, categories, per-category gain, one master, mute-on-blur. It is asset-based, so it has no sounds of its own. |
| [Tone.js](https://github.com/Tonejs/Tone.js) | 15.1.22 | MIT | 338 kB | **Only when sound must be continuous and parametric** — a scan, a load, a threat level responding to state. Enormous for discrete bleeps, and the only option for anything else. |
The composition is the finding: **synthesise with ZzFX, manage with Arwes.**
Specimen 17 encodes ZzFX's samples as WAV data URLs and hands them to Arwes'
manager, which gets both halves with zero binary assets in the repository.
**The seventh pass put that verdict on trial** rather than repeating it.
49-nightside runs the same rain through both libraries and measures what it
costs to *keep a sound going*, which is a different question from what it costs
to download one and gets a different answer: for Tone intensity is a parameter
and the node count does not move, for ZzFX intensity is an event rate and the
main thread pays per drop. The verdict survives, with one correction that is not
about either library — see finding 77, where the naive driver lost the argument
on its own page. Two smaller confirmations: **Tone constructs an AudioContext on
import too**, so the trap this section found in ZzFX is not specific to it, and
**Tone needs no audio assets either** as long as you are willing to synthesise a
street rather than record one.
Three rules the shared layer enforces ([`lab/sound.js`](lab/sound.js)):
default off, one control, and lazy — no `AudioContext` until a real click.
The laziness is forced: importing `vendor/zzfx.js` constructs an `AudioContext`
at module scope, so the *import itself* has to be deferred.
---
## 8 · Instrumentation
| project | ver | licence | size | verdict |
|---|---|---|---|---|
| [uPlot](https://github.com/leeoniya/uPlot) | 1.6.32 | MIT | 50 kB | **The right chart library for this genre precisely because it has no animation layer to switch off.** It clears and repaints the whole canvas per update and is fast enough that this is a non-issue. |
**The lab's one anti-motion finding.** Easing is interpolation, and interpolating
measured data draws values nothing ever reported. On a marketing dashboard that
is a nice touch; on an instrument someone makes a decision from it is a
fabrication. Specimen 20 runs the same signal raw and through a spring side by
side and names, in the units of the instrument, how far the eased lane is
currently lying.
---
## 9 · Interaction / input
The strand the lab did not have. Everything before it is something you watch;
23-shell was the only specimen that took a keystroke, and it was filed under
Terminal because there was nowhere else to put it. It now lives here.
| project | ver | licence | size | verdict |
|---|---|---|---|---|
| [Split.js](https://github.com/nathancahill/split) | 1.6.5 | MIT | 2 kB | **The smallest useful thing in `vendor/`.** Keeps siblings summing to their container while you drag the seam between them, and stops. No stylesheet — the gutter is invisible until you give it one, which is a feature at 2 kB. No keyboard *at all*: the gutter is a bare `