Skip to main content
View as Markdown

scenes/<id>.yaml

One file per scene. The filename is the scene idscenes/demo.yaml is the scene demo. Do not put an id: field inside; the loader injects it, and a stray id: is a legible error.

A scene is a background (what fills the frame) + an optional avatar overlay + narration.

# yaml-language-server: $schema=../../../schemas/scene.schema.json
background: { kind: web, source: dashboard_demo } # slide | image | cli | web | video | avatar
avatar: { placement: pip } # off (default) | pip | fullscreen
narration: Here is the live dashboard.
duration: null # null for cli/web/video/avatar; a number for slide/image
chapter: Dashboard

background

What fills the frame. The kind selects the visual; most kinds reference a source id from sources.yaml:

kindSource of visual
slideAn HTML template (no source needed)
imageAn image source
cliA cli (terminal) source — executes the tape (records a real terminal via VHS)
webA web (browser) source
videoA video clip source
avatarA generated talking-head

Pasting a CLI transcript to show — not run — a terminal session? Use the terminal slide template (below), not this cli background: the cli kind above executes the tape for real (VHS screen-records it); slide/terminal only renders pasted text, nothing runs.

Video backgrounds

A video background uses a pre-recorded clip. It can carry a frame mat — the clip inset centered over a solid color or an image source:

background: { kind: video, source: demo_clip, frame: { color: "#0b0f19", scale: 0.8 } }
# or frame: { image: backdrop } where `backdrop` is an image source

A bare fullscreen video scene (no effects, frame, or avatar) renders via an ffmpeg-only passthrough — it skips the headless-Chrome compositor entirely, so it's faster.

Slide templates

A slide background names a template + its data:

templatedata
title{ title, subtitle? } — a centered statement card.
bullets{ title?, items: [...] } — an enumerated list.
diagram-3col{ title?, cols: [...] } (≥2) — boxes connected left-to-right.
gradient{ from, to, accent, angle?, accentOpacity?, motion?, headline?, subtitle? } — a colored/gradient background with a slow two-layer parallax drift. No image, renders instantly, never fails.
terminal{ transcript, prompt? } — a pasted CLI transcript rendered as a styled command/output window. A line prefixed by prompt (default $) renders as a command; any other line renders as output. Nothing is executed — this is a static look built from pasted text, not the cli/VHS background below.
background: { kind: slide, template: gradient, data: { from: "#0A0B10", to: "#12141C", accent: "#818CF8", angle: 135 } }
background: { kind: slide, template: terminal, data: { transcript: "$ npm install\nadded 42 packages" } }

avatar

An optional talking-head overlay:

avatar: { placement: pip } # off (default) | pip | fullscreen

narration

The spoken script for the scene. Decodx generates the voice — the words are the editing surface. Multi-line reads best as a block scalar:

narration: |
Welcome to the dashboard.
Everything you see here updates live.

duration — authored vs. auto

Scene kindduration
slide, imageAn authored number (seconds) — you set how long it holds.
cli, web, video, avatarnull — the length is locked automatically from the recording or clip.

Retiming a video: speed

A video background can be retimed:

  • a numberspeed: 1.5 is 1.5× faster (0.8 slower); the scene duration becomes clip / speed.
  • speed: auto — let the narration set the pace: the clip is stretched or compressed to last exactly as long as the narration (+ padding), clamped to [0.9, 2.5]×.
  • project-wide default: meta.defaults.video_speed (a per-scene speed still wins).

If narration covers less than 70% of a fixed-speed clip, decodx narrate prints an underrun advisory with a words-to-add hint and the speed: auto pointer.

Editing without pre-cutting: trim / cuts / crop

Author edits on the background instead of pre-cutting the file — so one recording can back many trimmed scenes:

FieldEffect
trim: [start, end]Source seconds — drives the scene duration.
cuts: [[in, out], …]Remove dead-time spans; kept segments are concatenated.
crop: [x, y, w, h]A fractional (0–1) window of the frame.

All three are applied in one cached ffmpeg pre-pass.

chapter

An optional chapter label — collected into output/chapters.txt (paste-ready YouTube chapters) and chapters.json.


That's the full authoring surface. To generate a whole scene set from a recording instead of writing these by hand, see decodx ingest in the Quickstart.