Skip to content

Remotion — Practice Reference (kn86-attract pipeline)

Remotion is a TypeScript/React framework for programmatic video composition — you describe each frame of a video as React components, the renderer rasterizes them, and @remotion/renderer bakes the result to MP4, WebM, PNG sequences, or GIF via headless Chromium + ffmpeg. The project ships:

PackageRole
remotionCore API: Composition, Sequence, useCurrentFrame(), useVideoConfig(), etc.
@remotion/cliStudio UI for interactive preview + scrubbing
@remotion/bundlerBundles React composition source into a renderable artifact
@remotion/rendererHeadless renderer (Puppeteer + Chromium) — produces video files
@remotion/lambdaOptional: AWS Lambda batch-render fanout (not used by kn86-attract)

The model is stateless and frame-keyeduseCurrentFrame() returns the integer frame index, and every visual is deterministic for that frame. Same frame number → same rendered pixels. This determinism is the reason it’s a fit for KN-86’s needs.

kn86-attract (at ~/src/kinoshita/kn86-attract/) is a standalone Remotion pipeline that renders KN-86 terminal attract-mode video assets for the marketing site and for in-cart preview clips. Pinned to Remotion 4.0.448 (package.json lines 16-22).

Structure:

PathRole
src/Root.tsxComposition registry — registers ~130 individual scenes + 16 cartridge demo sequences (feature + full-run pairs) + the boot sequence. Compositions: 1920×1080 (primary display) and 1024×256 (CIPHER-LINE OLED, 4× scale of native 256×64).
src/scenes/library/~130 individual scene files (boot-post.ts, icebreaker-network-map.ts, etc.). Each scene is a list of timed events (at(seconds, event)).
src/scenes/types.tsScene-event grammar — text, clear, scroll, cursor, audio, statusBar, actionBar, etc. Matches the cartridge attract-clip format.
src/renderer/TerminalGrid.tsx80×25 primary grid React renderer. Press Start 2P, amber #E6A020, black #000000.
src/renderer/OledGrid.tsx4×32 CIPHER-LINE secondary OLED renderer. Yellow text on black, slower typing cadence (~18 chars/sec vs ~40 for primary).
src/sequences/16 cart demos plus the boot sequence. Each chains scenes with cut/fade/flash transitions.
tools/export-demo.tsHeadless .kn86clip binary exporter (deterministic scene → frame-grid → encoded binary for emulator playback).
tools/simulator.tsFrame-by-frame event engine — mirrors TerminalGrid.tsx logic in headless form. Same logic, two entry points.
tools/encoder.tsBinary region-compression codec for .kn86clip files.
tools/publish-to-site.tsBatch renders webm/mp4/jpg poster frames for the marketing site (target: ../kn86-site/public/clips/manifest.json).

kn86-attract is not the runtime renderer — the deck itself renders via SDL3 + the nOSh C runtime per ADR-0025. Remotion exists alongside the runtime for three purposes:

  1. Marketing-asset pipeline. Every cart deserves a 10-30 second attract-mode video on the kn86-deckline.com marketing site. Authoring those in Remotion (in the same grid, same font, same colors as the live runtime) means the videos look exactly like the device — no PowerPoint mockups, no Photoshop simulations, no “trust me bro” stylization.
  2. Deterministic clip playback inside carts. .kn86clip binaries (produced by tools/export-demo.ts) are pre-rendered frame-grids the emulator/runtime can play back without running a Lisp scene script. Smaller, faster, hermetic. The cart-grammar clip-system.md (docs/software/cartridges/authoring/clip-system.md) is the runtime-side counterpart.
  3. Scene-grammar parity validation. The Remotion scene types (src/scenes/types.ts) match the in-runtime cart attract-clip format. If the grammar diverges, scenes authored for the marketing site won’t render on the device — so the two grammars are kept in lockstep.

Beyond the existing kn86-attract integration, the corpus argues for a few specific transferable patterns:

  1. Frame-keyed event model. Scenes describe what happens at frame N (at(2.5, text(0, 0, "hello", 40))), not how to render incrementally. Stateless, reproducible, easy to test. This is the right pattern for any future “deterministic playback of canned content” — cart intro splashes, cipher-voice scripted sequences, mission-briefing canned animations.
  2. Dual-renderer architecture. React renderer for preview + interactive studio (npm run dev → Remotion Studio); headless simulator for batch export (tools/simulator.ts). Keeps dev fast (hot-reload, scrub-the-timeline) while enabling artifact generation. Pattern is reusable for the upcoming (reveal …) NoshAPI primitive (effect/ascii-effects.md) — a React preview + a C-side production renderer.
  3. Binary clip format. Pre-computed frame grids compressed into .kn86clip files allow playback without shipping a runtime. The encoder/decoder is in tools/encoder.ts. Reference for any future “bake this Lisp procedure to a sealed playback artifact” feature.
  4. Status/action bar abstraction. statusBar and actionBar events in src/scenes/types.ts decouple Row 0 / Row 24 chrome from cart content. Simplifies scene authoring; the cart author doesn’t think about chrome, just the content rows.
  5. Timing helpers. at(seconds, event) converts seconds → frames internally. postLineDuration() calculates dot-animation length up-front. Reduces frame-count arithmetic errors. Pattern transfers to any timing-sensitive procedural content authoring.
  • Not adopting Remotion as the runtime renderer. The deck runs SDL3 + C, not React + Chromium. Remotion is a marketing/asset-pipeline tool, not a runtime.
  • Not using @remotion/lambda. AWS Lambda batch-render fanout is overkill for the current asset count. Local renders are fast enough.
  • Not authoring carts in TypeScript. Carts are authored in KEC Lisp per ADR-0001. The Remotion scene-grammar overlap is for marketing clips, not cart code.
  • package.json lines 16-22 — Remotion package pins (4.0.448).
  • src/Root.tsx lines 94-129 — composition registry; dimensions = 1920×1080 (primary) + 1024×256 (OLED 4× scale).
  • src/scenes/types.ts — full scene-event grammar.
  • src/renderer/constants.ts — 80×25 grid, 30 FPS, amber #E6A020, Press Start 2P.
  • src/renderer/oled-constants.ts — 256×64 native, 32×4 char, 4× scale = 1024×256 in Remotion.
  • tools/publish-to-site.ts lines 23-45 — SITE_CLIP_IDS curated marketing set.
  • tools/export-demo.ts + tools/simulator.ts — headless frame engine.
  • Cross-link effect/ascii-effects.md — the project-wide ASCII visual language doctrine. The (reveal …) NoshAPI primitive proposed there should compose with the Remotion scene grammar — same effect, two render targets (Remotion for marketing, libcaca-style for runtime).
  • Cross-link docs/software/cartridges/authoring/clip-system.md — the runtime-side counterpart of .kn86clip playback.
  • Cross-link docs/adr/ADR-0014-display-profile-redesign.md — the 12×24 / 960×600 display profile that Remotion compositions render against.
  • Versioning: Remotion 4.x is a major rewrite from 3.x with breaking changes; the kn86-attract pin to 4.0.448 is post-rewrite. Upgrading within 4.x should be safe; jumping to 5.x will need a migration cycle.