Skip to main content
The decktalk command runs each build stage on its own, or all seven stages with decktalk build. This page lists every command with its options, the files it reads and writes, its output, and its exit codes.

Global options

These options work with every command.
  • -v and -q go before or after the command name.
  • -p goes before the command name, or after it on every command except init, setup, and doctor.
  • Every other option goes after the command name.
  • A repeatable option takes one value each time. Write --only 3 --only 5, not --only 3,5.
  • Logs go to stderr. Tables and JSON go to stdout.
  • A command reads each key from the environment first, then from .env in the project.
  • DeckTalk never prints a key.

Environment variables

Note: a key value that starts with < counts as unset, so the placeholders in .env.example never reach the provider. Configuration lists every tuning field.

Setup commands

decktalk setup

Downloads Chromium, ffmpeg, and KaTeX, one time per machine.
Reads nothing. Writes Chromium to Playwright’s cache, ffmpeg and ffprobe into the Python environment through static-ffmpeg, and KaTeX to the DeckTalk cache.
  • On Linux, setup also installs Chromium’s system libraries. What setup downloads lists the paths and the permissions it needs.
  • A download that already exists is not downloaded again.
  • If the KaTeX download fails, setup warns and still exits 0. Pages then load KaTeX from a CDN.

decktalk doctor

Reports each tool that DeckTalk needs and where it found the tool.
Reads Playwright’s Chromium (it launches it), the ffmpeg locations, the per-machine config path, and the KaTeX cache. Writes nothing. doctor never downloads anything. Note: a MISSING row is a certain finding. A warning row is an uncertain finding, so it exits 1 only with --strict. When ffmpeg is not found, doctor prints one ffmpeg MISSING row and no ffprobe row. In the JSON, a warning row has "ok": false and "required": false.

decktalk init

Writes the scaffold into a new directory.
Reads the packaged template and the KaTeX cache. Writes decktalk.toml, script.md, cues.json, .gitignore, .env.example, media/markers.json, and deck/. deck/ holds index.html, lesson.html, the fonts, decktalk-runtime.js, and katex/ when KaTeX is cached. If KaTeX is not cached, init warns and writes CDN tags into deck/index.html. The warning starts with KaTeX is not cached, so the pages in deck/ load it from a CDN.

decktalk runtime

Copies the packaged page runtime into the project.
Reads nothing but the project tree. Writes every decktalk-runtime.js under the project, or deck/decktalk-runtime.js when the project has none. Run it after you upgrade DeckTalk.

decktalk preflight

Checks a voiced build before it spends credits or records anything. It plans the takes, resolves every cue, and estimates each reveal from frozen frames of the pages.
Reads script.md, cues.json, decktalk.toml, the pages, and build/audio. Needs no key. Writes build/preflight/NN/step-<id>-cue-<cue>.png and step-<id>-before-<cue>.png, after it empties build/preflight. --no-frames writes nothing. preflight never writes beats.json or build/audio. preflight runs three checks.
  1. Takes. It prints the take plan of narrate --dry-run. The plan says which sections a voiced run would voice, keep cached, or move, with the characters each section sends.
  2. Cues. It resolves every cue phrase as beats does, with the same notes. A cached or moved take resolves on its own words. A section that would be voiced resolves on estimated words at silent_words_per_minute, so its cue times are placeholders.
  3. Frames. For each cue, it freezes the page just before the cue and at the cue, and compares the two frames as verify compares frames. For each page section with carries_previous = true after a page section, it compares the previous section’s last frozen frame with this section’s first one.
A frozen frame shows every reveal in its end state, with nothing in motion. So chg % estimates what verify measures once the reveal has settled, with a control of 0. The verdicts use the [verify] limits.
  • The frames follow cue mode. The first cued step mounts at t=0, and every other step mounts at its earliest cue. The frame before a cue that mounts its step shows the step that was on screen.
  • A freeze fires a step’s cues in autoplay order. When the cue times run in another order, the row notes that the frames may differ from the recording.
  • The estimate sees no motion, no camera push, and no close neighbor cue. verify measures those on the final mp4.
  • The frames need catalog[].cues and &before= from the runtime. A page with an older copy of decktalk-runtime.js skips every cue as RUNTIME_OUTDATED.
These are parts of a run on the scaffold after decktalk narrate --silent, with carries_previous = true on section 2.

decktalk build

Runs all seven stages in order and prints each stage’s table.
Note: with --only, narrate still runs on every section and assemble still uses every section. Recordings of other sections stay as they are. A silent build stops when the project has voiced takes, as A silent build and a voiced build explains. Reads and writes what each stage reads and writes, below. build does not measure cues. It exits 0 on TRUNCATED, STALLED, NO COVER, BLACK?, and KATEX?. To gate a script on the whole video, run check and verify after the build, as Build in CI and offline shows. These are the last lines of a silent build of the scaffold.

Stages

build runs these seven commands in this order. Each one also runs on its own.

decktalk narrate

Voices the script and writes each section’s audio and word times.
Reads script.md, decktalk.toml, and .env. --silent and --dry-run need no key. Writes build/audio/NN-slug.mp3, NN-slug.words.json, manifest.json, narration.mp3, and timeline.json. --dry-run writes nothing.
  • narrate never voices a clip section.
  • narrate caches each section by provider, voice id, model, output format, voice settings, and text.
  • --only rebuilds narration.mp3 and timeline.json from every voiced section, not only the named ones.
  • --only with no spoken section fails, for example no spoken sections match [5]; spoken sections are [1, 2, 3, 4, 6, 8, 9].
  • A placeholder fails a voiced run with unfilled placeholders [...] in the script.
The first table gives each section’s word count, estimate (est), time budget (target), and audio length (actual). The second table is the timeline in narration.mp3. script.md gives the estimate formulas.

The take plan

--dry-run checks the narration cache the way a voiced run does, and ends with the take plan. Each section gets one take status. A synthesize row gives its reason: no take yet, only a silent take exists, the text, voice, model, or voice settings changed, the file name changed from <file>, the mp3 or the words file is missing, or forced. sent counts the characters the voice receives, with dashes and break tags. spoken counts the spoken words’ characters. Without a key, a project with no voiced take plans every section as synthesize. A project with voiced takes plans them as unknown, because the cache key needs the voice id.
With --json, the payload under narrate has these keys.

decktalk beats

Resolves each cue phrase to a cue time and writes beats.json.
Reads manifest.json, the words files, cues.json, and each page. Writes beats.json and beats.anchors.json, even when it exits 1. beats looks for each cue id in its page as a quoted literal, in single quotes, double quotes, or backticks. A data-cue value, a key of a step’s cues object, and a handler key all count. Cues gives the phrase matching rules. The table columns are sec, speech (the speech end in seconds), need (min_seconds), and cues (each id@seconds). A note line starts with !. Note: a clip section with cues prints -- and (no narration (clip section, or not rendered)).

decktalk record

Records each page section in headless Chromium to a webm.
Reads timeline.json (not with --seconds), beats.json (not with --no-beats), and the pages. Writes build/rec/NN-scene.webm and its sidecar NN-scene.json.
  • record prints no table. It logs one [rec ] line per section and every page warning as [page].
  • record skips a page section that has no span and logs no narration span yet; skipped.
  • If the page stalls after narration t=0 for longer than [align] stall_ms (150), record records the section again.
  • record tries again up to [record] retries (2) times.

decktalk measure

Finds narration t=0 in each recording and writes it to the sidecar.
Reads each webm and its sidecar. Writes lead_in_seconds and lead_method into the sidecar. measure scans the first [align] scan_seconds (4.0) of the recording and finds the last magenta frame. Narration t=0 is one frame after it. The table columns are trim (t=0 in the webm), wall (the recorder’s own clock estimate), and method.

decktalk check

Checks each recording’s length, brightness, and recorder warnings.
Reads each webm and its sidecar. Writes nothing. Each row describes one recording. Note: one row can show several verdicts, separated by spaces. The JSON lists them in verdicts and puts the stall length in stall_ms.

decktalk assemble

Cuts, mixes, and writes the final video with its captions and chapters.
Reads timeline.json, manifest.json, beats.json, the recordings and sidecars, the clips, the markers file, the underscore, the ambience bed, and the sound effects. Writes build/out/NN-section.mp4, <name>.mp4, a copy named <name>-YYYYMMDD-HHMM.mp4, <name>.srt, <name>.vtt, <name>.chapters.txt, and build/out/slates/.
  • Without --strict, a missing clip plays a titled slate for slate_seconds, and a missing recording plays black.
  • assemble trims each recording at the lead_in_seconds that measure wrote. If measure has not read the recording since it was recorded, assemble prints STALE MEASUREMENT. The section may then play early or late. Without --strict, assemble warns and continues. With --strict, it stops.
  • A missing underscore, ambience bed, markers file, or sound effect logs a warning and plays without it.
  • A silent build skips the loudness pass, so --strict has no loudness to fail on.
  • assemble prints the final path and length on stdout.

decktalk verify

Checks section starts, cuts, cut continuity, and cues in the final video.
Reads build/out/NN-section.mp4, build/out/<name>.mp4, narration.mp3, timeline.json, beats.json, beats.anchors.json, manifest.json, and cues.json. Writes nothing.
  • With no cue arguments, verify checks every cue in beats.json, by section and then by cue time.
  • With no cue arguments, verify skips a cue that cues.json marks "verify": false.
  • Named cues run in the order given, positional cues first. A named cue runs even when it is opted out.
  • --only also filters named cues. A named cue outside --only gets no row.
  • The verify stage inside build checks starts, cuts, and continuity, and no cue.
verify prints up to four tables: section starts, cuts, cut continuity, and cues. The continuity table appears only when a section sets carries_previous. A clip section has a start row but no cut row. cut at is where the section’s narration ends in the final video. The cue table gains an a/v column only when at least one measured cue has an a/v value. The JSON silent key says if the build was silent. verify defines each column. Note: OFF CUE prints no distance. Compare the offset column with 80 ms (max_offset_frames) and the a/v column with 120 ms (max_av_frames). A measured cue with no click shows - in the a/v column and has the JSON reason NO_CLICK. NO_CLICK is not a failure. verify explains each reason.

decktalk soundscape

Generates the underscore, the ambience bed, and sound effects from prompts.
Reads [soundscape], the [mix] paths, and .env. Writes each audio file with a .manifest.json beside it. Music longer than [elevenlabs] max_music_chunk_seconds also writes -partN.mp3 files. soundscape spends credits, as What spends credits explains. Each item prints == <name> -> <path> [<status>], then its endpoint and request. A project with no prompts prints nothing to generate.

Inspection commands

decktalk shots

Writes review screenshots of steps, or frames from a playing section.
Note: the <page>/ folder, such as index/ or lesson/, appears only when shots covers two or more pages. --page with one file writes no folder. Section frames never use one.
  • --cue needs exactly one --step and does not combine with --section.
  • A frozen step fires its cues in autoplay order, up to the named cue. Runtime describes freeze mode.
  • An --at value past 30 s fails with error: TimeoutError: Page.wait_for_function: Timeout 30000ms exceeded.
Reads the pages. With --section, also beats.json and timeline.json. Writes the PNGs.

decktalk status

Prints what the project has and what is built.
Reads the project files and the build directory. It probes only the final video. Writes nothing. Note: the timeline shows narration times. A clip section adds its length to the final video only, so final is longer than narration total. Time bases explains the clocks.
This shape shows the --json payload. The values are types, not a sample.
status exits 0 unless the project does not load.

Clip commands

decktalk words

Prints each spoken section’s words, with times in seconds after the section starts.
Reads timeline.json, and manifest.json for the script’s spelling. Writes nothing.
  • Each time counts from the section’s own start, as ?words= and beats.json do. Time bases explains the clocks.
  • A section’s lead_seconds counts, so its first word starts after the lead.
  • Each row gives the word’s start, its end, and the word with the script’s punctuation and case.
  • After a silent build, the times are estimates, and each section header says estimated.
  • A page that prints real word times can take them from this output.
This shape shows the --json payload. The values are types, not a sample.
word is the word as the voice returned it, with punctuation removed. text is the same word with the script’s punctuation and case.

decktalk clip

Cuts a span of a built page section into a video file and a words file, for a clip section.
Reads build/out/NN-section.mp4, the section’s mp3, manifest.json, and timeline.json. Writes the clip and its words file.
  • --from and --to use the clock that decktalk words prints. Run it first to pick the span.
  • The picture is the section video’s frames from --from to --to, each time rounded to the nearest frame.
  • The sound is the section’s mp3 over the same span. A span inside the section’s lead_seconds is silent.
  • The sound fades in and out over 10 ms, so a cut inside a word does not click.
  • The clip is H.264 video and AAC audio, at the frame rate and sample rate in [video].
  • The words file lists each word wholly inside the span, in seconds after the clip starts, with the script’s punctuation and case. A word that the span cuts in two is left out, and clip prints a warning.
  • After a silent build, the sound is the click track and the words are estimates, and clip prints a warning.
The line gives the clip’s length with the hold, the frames it took, the span after rounding to whole frames, the hold, and the gain. --from 2.87 rounds to frame 72, at 2.88 s. --hold 0.5 rounds to 12 frames, which is 0.48 s. Cut a clip from a build puts the clip in a clip section.

Output and exit flags

status, beats, preflight, check, verify, and doctor take these three options. An uncertain finding is a verdict that ends in ?. In beats and preflight, speech shorter than min_seconds is also uncertain. On assemble and build, --strict has a different meaning. It fails on a missing recording, or a missing clip in a section without optional = true. This shape shows the JSON envelope. The values are types, not a sample.
Note: each beats section is {key, speech_end, min_seconds, skipped, cues, notes}. Each note is {cue, verdict, detail}, where verdict is UNRESOLVED, UNKNOWN, or null. Each verify cue row is {section, cue, cue_seconds, final_seconds, changed_percent, control_percent, offset_ms, av_ms, verdict, reason}. verify defines them. Each preflight cue row is {section, cue, cue_seconds, step, changed_percent, verdict, reason, detail, note, before, after}, and each carry row is {key, changed_percent, verdict, reason, detail, last, first}. The preflight takes rows are the take plan rows without text, and beats is the beats payload without beats_file, plus estimated_sections.
  • Every key is a two-digit section string such as "03". A verify cue row’s section is an integer.
  • A value that does not exist is null. Every verdict is the name that the table prints.
  • Paths are relative to the project root, with forward slashes. project.root, doctor details, and paths outside the project stay absolute.
  • Only the JSON object goes to stdout, so decktalk verify --json > verify.json captures nothing else.

Exit codes

  • --no-fail makes doctor, beats, preflight, check, verify, and status exit 0 on findings.
  • With --no-fail, the JSON ok key still says if the command passed.
  • An error is not a finding. A DeckTalkError exits 1, even with --no-fail.
  • An unexpected exception prints error: <type>: <message> (add -v for the traceback) and exits 1.
  • With -v, an unexpected exception prints its full traceback.