Skip to content

KN-86 ASCII Effect Spec

⚠ PROMOTED — partial. As of 2026-06-07 (ADR-0033), the (reveal …) portion of this spec has been promoted to canonical at docs/software/api-reference/grammars/reveal-styles.md. Cart authors and runtime engineers should read the canonical doc — not this one — for the (reveal …) primitive contract, style enum, lifecycle, parameter ranges, and Pi Zero 2 W performance budget. The (reveal …) discussion below (§“Interaction grammar” item 3 and §“NoshAPI surface” reveal / unreveal snippets) is preserved for design-history narrative only.

The remaining surfaces in this doc — the :hover / :click interaction grammars, (draw-ascii …), project-wide defaults like the 72-char ramp and charMode = shape — remain inspiration-tier with no canonical promotion. A future ADR may promote them if a real v0.1 caller surfaces. For now: read this doc as future-scope reference, not as a binding contract.

  • Source: distilled from react-video-ascii and libcaca, Batch 6
  • Category: effect — interaction-grammar spec for ASCII rendering across KN-86 surfaces
  • Role for KN-86: the single project-wide grammar for how KN-86 renders imagery + reacts to hover, click, and load events. The whole TUI shares one ASCII visual language defined here. (The (reveal …) portion is now canonical at reveal-styles.md per ADR-0033; the rest remains inspiration-tier.)
  • License / caveats: this is KN-86’s own spec, distilling props from MIT-friendly published projects. Independent implementation; no vendored code.

Batch 6 introduces ASCII rendering as a first-class KN-86 capability — converting raster imagery (splash art, cart preview thumbnails, album-cover-equivalents, boot art) and interactive surfaces into colour ASCII / Unicode text. This spec defines the project-wide defaults and interaction grammar so every cart, every surface, every animation hand-shakes against the same vocabulary.

Two batches of prior art compose into this spec:

  • react-video-ascii is the interaction-grammar north star. Its props (numColsRaw, charMode, mouseEffect, clickEffect, revealEffect) define the operations and tunables KN-86 should expose to cart authors through NoshAPI.
  • libcaca is the engine north star. Its image→text pipeline (img2txt, charset palettes, dithering modes) defines how KN-86’s runtime actually rasterises an image down to amber cells.
  • asciinator (Batch 3) is the technique foundation — halfblock- with fg+bg color, three rendering modes (halfblock, shade, ascii), 24-bit-color emit format.

These are the canonical KN-86 defaults. A cart that doesn’t override these uses these.

SettingKN-86 defaultRangeNotes
numCols15020–350Columns of ASCII output. 150 is the react-video-ascii midpoint and reads cleanly at the KN-86 80×25 grid scale when the imagery occupies a sub-region of the cart-content area. Carts rendering full-width imagery use 80 (the canonical column count).
charModeshapeshape | luminanceshape matches glyph silhouettes to image features; luminance uses brightness only. KN-86 commits to shape as the default because it preserves edge detail at amber-on-black where luminance-only loses high-frequency content.
charsstandard ramp (see below)Verbatim from react-video-ascii.
brightness1.00.0–2.0Multiplier. The aesthetic-mode AMBER palette (Batch 2 visual identity brief) sets the absolute intensity; this is the per-render boost.
saturation0.00.0–2.0KN-86 is monochrome amber; saturation is structurally zero. Carts that ship in a multi-color emulator preview can override; on-device renders ignore.
bgOpacity0.30.0–1.0Background-cell intensity below the foreground glyph. Low bgOpacity keeps the device looking like text-on-black; higher bgOpacity reads more like image-with-text-overlay.
cropFocuscenterleft | center | rightWhere to anchor cropping when the source image’s aspect doesn’t match the render target’s aspect.

The canonical KN-86 character ramp, dark to bright (verbatim from react-video-ascii):

`.',-_:!;|\"~+^lr`\/L`>t<v=Tz?icf1{sIxY*jJno}CZyVwmSXRqM$O%#9&NW0Q@

Length: 72 characters. Includes punctuation (low density), brackets and slashes (mid density), upper-case letters and symbols (high density), terminating in @. The ramp is the KN-86 project default for ASCII rendering of static imagery in shape mode; cart authors who want a different ramp pass their own.

Cart-author alternative ramps to know about

Section titled “Cart-author alternative ramps to know about”

Three sub-ramps worth documenting as named alternatives (not v0.1 defaults, but cited for cart authors):

  • tight-72 — the default above
  • classic-9 — the historical short ramp @%#*+=-:.` — 9 characters; reads chunky, deliberately period-correct
  • ascii-only — strip the default ramp to pure 7-bit ASCII (drop \\, ^, ~) for terminals that don’t render extended characters consistently

Borrowed directly from react-video-ascii’s props matrix; each becomes a NoshAPI primitive cart authors can attach to any ASCII surface.

1. Hover / mouse effect — (:hover style :brighten :options {...})

Section titled “1. Hover / mouse effect — (:hover style :brighten :options {...})”

KN-86 doesn’t have a mouse, so “hover” translates to keyboard-cursor position over an ASCII surface. The character cell the deck’s focus indicator currently overlaps is the “hovered” cell.

Two styles, both inherited from react-video-ascii:

Hovered cell + a trailing wake brighten relative to baseline.

ParamKN-86 defaultRangeNotes
trailLen150–30Cells tracked in the trail behind the focus cursor. Higher = more lingering wake.
brightness2.00.2–5.0Brightness multiplier at the cursor cell, fading to 1.0 across the trail.
trailDecay0.850.0–1.0Per-step exponential decay of brightness along the trail.
radius0.080.03–0.2Effect radius as a fraction of the smaller canvas dimension.
duration1.00.1–4.0Seconds the effect lingers after focus leaves the cell.

Hovered region’s characters are replaced with chars from a scatter set (e.g., '->o').

ParamKN-86 defaultRangeNotes
scatterChars'->o'Substituted character set.
radius0.050.03–0.2Same shape as brighten radius.
duration0.50.1–4.0Seconds the scatter lingers.

2. Click / commit effect — (:click style :ripple :options {...})

Section titled “2. Click / commit effect — (:click style :ripple :options {...})”

KN-86’s “click” translates to a commit keystroke on the focus cell — typically Enter, the link-hint label key, or a verb dispatch. Two styles:

A brightness ring radiates outward from the committed cell.

ParamKN-86 defaultRangeNotes
speed3.00.5–10.0Cells-per-second the ring expands.
brightness1.11.05–2.0Brightness boost of the ring relative to baseline.
duration1.00.1–4.0Total seconds before the ring fades to baseline.

A scatter region expands outward from the committed cell.

ParamKN-86 defaultRangeNotes
spreadSpeed7.50.5–10.0Speed of the spread wave front.
spreadExpandDuration1.50.5–5.0Seconds for the region to fully expand.

3. Reveal effect on load — (:reveal style :random :options {...})

Section titled “3. Reveal effect on load — (:reveal style :random :options {...})”

When an ASCII surface first appears, the characters reveal in sequence rather than appearing all at once. Three patterns:

StyleWhen to use
diagonalTop-left to bottom-right wipe. Reads as deliberate, structural. Use for committed/important surfaces (mission accept screen, the boot animation handoff to the first frame).
radialReveal expands outward from center. Reads as birth. Use for surfaces that “appear into being” — cart-load, a new CIPHER fragment arrival on the OLED.
randomCells reveal in random order. Reads as static collapsing to signal. Use for lower-stakes surfaces; matches the no-more-secrets decryption-reveal sibling. Project default.
ParamKN-86 defaultRangeNotes
typerandomdiagonal | radial | randomProject-default random; carts override per use case
duration0.40.1–4.0Total seconds for the full reveal.

Composition with existing KN-86 visual effects

Section titled “Composition with existing KN-86 visual effects”

The Batch 4 + 5 synthesis already committed to three signature visual effects. This spec composes with all three:

Existing affordanceInteraction with this spec
Boot animation (BOOTSTRA.386 + AetherTune)The boot animation can use the reveal:diagonal pattern from this spec to hand off into the first interactive frame
reveal-text (no-more-secrets decryption reveal)Same family as reveal:random here. Unify under one NoshAPI surface(reveal ...) takes a :style arg covering both the per-cell ramp-flicker (no-more-secrets) and the ramp-by-position (react-video-ascii) cases.
CRT power-off animation (AetherTune)The power-off shrink uses the inverse of reveal:radialunreveal:radial collapses the frame inward.
Carousel auto-rotation (blessed-contrib)Each carousel tab change uses reveal:diagonal for the inbound tab.

The cart-author API exposed through NoshAPI (ADR-0005) for the ASCII-effect grammar. Names indicative; exact symbols to be finalized in the FFI catalog.

(draw-ascii x y w h image &key chars char-mode brightness saturation bg-opacity crop-focus)
;; Render a raster image as ASCII into the rectangle (x y w h).
(ascii-effect surface :hover :brighten :trail-len 15 :brightness 2.0 :radius 0.08 :duration 1.0)
;; Attach a hover effect to an existing ASCII surface.
(ascii-effect surface :click :ripple :speed 3.0 :brightness 1.1 :duration 1.0)
;; Attach a click/commit effect.
(reveal surface :random :duration 0.4)
;; Apply the load-reveal animation. Composes with no-more-secrets style via :style flag.
(unreveal surface :radial :duration 0.6)
;; Inverse — collapse the frame inward.

Promote items from the Batch 4 features matrix:

  • Item 45 (NoshAPI reveal-text primitive) absorbs into the (reveal ...) primitive specified here, with :style :char-flicker as the no-more-secrets variant
  • Item 9 (CIPHER-LINE visualizer) can now also use (draw-ascii ...) for cart-driven non-FFT imagery on the OLED
  • New item 58: ASCII-effect grammar shipped in v0.1 with the default ramp, charMode: shape, and the three event types (hover, click, reveal). See features-matrix update.
  • numCols default — react-video-ascii’s project-canonical default is 250; this spec sets KN-86 to 150 because 150 reads better against the AMBER monochrome aesthetic at the 80×25 panel scale. Worth validating with operator-eye test on the hardware bring-up; may move to 100 or 200 depending.
  • scatterChars for KN-86 — react-video-ascii ships '->o' as the default scatter set. KN-86 may want a CP437-friendly alternative (e.g., ' ░▒▓' blocks-only set) for a more period-correct retro feel.
  • Hover translates how, exactly? — KN-86’s input is keyboard-only. The current spec says “the cell the focus cursor overlaps.” But many surfaces don’t have a visible cursor; the focus is implicit (e.g., the currently-highlighted list row). Need to define focus-cell semantics surface-by-surface or commit to a deck-wide focus-cell rule. Park for runtime-spec PR.
  • Performance budget on the Pi Zero 2 W — react-video-ascii is WebGL2-accelerated; the Pi doesn’t have that. The engine reference is libcaca, which is CPU-only and well-suited to the Pi. Expect a frame-rate ceiling lower than the 60 Hz the react-video-ascii demo achieves; measure on hardware and document the realistic cap (likely 15–30 Hz for full-screen ASCII rendering on the Pi).