Remotion — Practice Reference (kn86-attract pipeline)
What it is
Section titled “What it is”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:
| Package | Role |
|---|---|
remotion | Core API: Composition, Sequence, useCurrentFrame(), useVideoConfig(), etc. |
@remotion/cli | Studio UI for interactive preview + scrubbing |
@remotion/bundler | Bundles React composition source into a renderable artifact |
@remotion/renderer | Headless renderer (Puppeteer + Chromium) — produces video files |
@remotion/lambda | Optional: AWS Lambda batch-render fanout (not used by kn86-attract) |
The model is stateless and frame-keyed — useCurrentFrame() 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.
How kn86-attract uses Remotion
Section titled “How kn86-attract uses Remotion”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:
| Path | Role |
|---|---|
src/Root.tsx | Composition 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.ts | Scene-event grammar — text, clear, scroll, cursor, audio, statusBar, actionBar, etc. Matches the cartridge attract-clip format. |
src/renderer/TerminalGrid.tsx | 80×25 primary grid React renderer. Press Start 2P, amber #E6A020, black #000000. |
src/renderer/OledGrid.tsx | 4×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.ts | Headless .kn86clip binary exporter (deterministic scene → frame-grid → encoded binary for emulator playback). |
tools/simulator.ts | Frame-by-frame event engine — mirrors TerminalGrid.tsx logic in headless form. Same logic, two entry points. |
tools/encoder.ts | Binary region-compression codec for .kn86clip files. |
tools/publish-to-site.ts | Batch renders webm/mp4/jpg poster frames for the marketing site (target: ../kn86-site/public/clips/manifest.json). |
Why this matters for the KN-86 project
Section titled “Why this matters for the KN-86 project”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:
- Marketing-asset pipeline. Every cart deserves a 10-30 second attract-mode video on the
kn86-deckline.commarketing 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. - Deterministic clip playback inside carts.
.kn86clipbinaries (produced bytools/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-grammarclip-system.md(docs/software/cartridges/authoring/clip-system.md) is the runtime-side counterpart. - 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.
Patterns to borrow
Section titled “Patterns to borrow”Beyond the existing kn86-attract integration, the corpus argues for a few specific transferable patterns:
- 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. - 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. - Binary clip format. Pre-computed frame grids compressed into
.kn86clipfiles allow playback without shipping a runtime. The encoder/decoder is intools/encoder.ts. Reference for any future “bake this Lisp procedure to a sealed playback artifact” feature. - Status/action bar abstraction.
statusBarandactionBarevents insrc/scenes/types.tsdecouple Row 0 / Row 24 chrome from cart content. Simplifies scene authoring; the cart author doesn’t think about chrome, just the content rows. - 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.
What we are explicitly NOT doing
Section titled “What we are explicitly NOT doing”- 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.
Key file references (kn86-attract)
Section titled “Key file references (kn86-attract)”package.jsonlines 16-22 — Remotion package pins (4.0.448).src/Root.tsxlines 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.tslines 23-45 —SITE_CLIP_IDScurated 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.kn86clipplayback. - 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-attractpin to 4.0.448 is post-rewrite. Upgrading within 4.x should be safe; jumping to 5.x will need a migration cycle.