> ## Documentation Index
> Fetch the complete documentation index at: https://docs.decktalk.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Reference card for agents

> Look up the whole DeckTalk contract on one page, with a link to the page behind each rule.

This card lists the whole DeckTalk contract on one page, and each group links to the page that
explains it. Use it to look up one rule, option, or exit code, or give it to an agent that writes a
project.

Every doc page is also served as plain text at [llms.txt](https://docs.decktalk.ai/llms.txt). The
whole site is one file at [llms-full.txt](https://docs.decktalk.ai/llms-full.txt).

## Inputs

You write four files, in this order: `script.md`, `decktalk.toml`, `cues.json`, and the page. All of
them live in the project directory. [Your first deck](/guides/first-deck) writes each one.

### `script.md`

* `## N. Title` starts section `N`.
* `## N. Title — 0:40 to 1:10` adds a time budget.
* `---`, `# Title`, or `## Title` with no number ends the section.
* A page section needs a `## N.` heading. A clip section does not.
* `[any text]` and `[beat]` make a beat. The voice reads a dash there.
* `[pause N]` sends `<break time="Ns" />`. No other direction sends a break tag.
* A written `<break time="1s" />` passes through as written.
* `[CAPS_1]` is a placeholder. Its pattern is `[A-Z][A-Z0-9_]*`.
* `narrate` refuses a placeholder without `--allow-placeholders`.
* `narrate` removes Markdown formatting before it sends the text.
* DeckTalk places `lead_seconds` (0.5) of silence before every spoken section's first word.
* DeckTalk places exactly `min_tail_seconds` of silence after every section's last word.
* The default `min_tail_seconds` is 0.7. The starter sets 1.3.
* The `est` column assumes 140 words per minute.
* A build without voice assumes 150 words per minute plus 0.7 s per beat.

Full rules: [script.md](/reference/script-md).

### `decktalk.toml`

* `[project]` holds `name` and the optional `script`, `cues`, and `build` paths.
* `[voice]` holds voice settings. The voice id and key live in `.env`.
* Each `[[section]]` has a `number` and an optional `chapter`.
* A page section adds `page` and an optional `scene`. `scene` defaults to `number`.
* A clip section adds `clip`, and the optional `slate_seconds`, `optional`, and `words`.
* `words` names a words file of the speech inside the clip, and the captions add it.
* Consecutive sections with the same `chapter` share one chapter.
* A clip section with `optional = true` plays its slate under `--strict` too.
* A clip section can sit before, between, or after page sections.
* A page section can set its own `lead_seconds` and `tail_seconds`, and `hold_seconds` to add silence. A hold pauses the narration.
* `[transition]`, `[mix]`, `[soundscape]`, and the tuning tables are optional.
* An unknown top-level table fails the load.
* DeckTalk warns about an unknown key inside a table, then ignores the key.

Full rules: [decktalk.toml](/reference/decktalk-toml).

### `cues.json`

This shape shows the file. It is not a sample.

```jsonc theme={null}
{"sections": {"N": {"min_seconds"?: 12, "cues": [
  {"cue": "id", "on": "phrase" | "$start" | "$end",
   "offset"?: 0.2, "occurrence"?: 2, "case_sensitive"?: true, "verify"?: false}
]}}}
```

* Each section key is a section number as a string. It must exist in `decktalk.toml`.
* `$start` is 0.0 s. `$end` is the end of the section's last spoken word.
* Matching splits the phrase and the spoken words on whitespace only.
* A hyphenated word is one token.
* Each token keeps only A to Z, a to z, 0 to 9, and the straight apostrophe.
* Matching ignores case unless `case_sensitive` is true.
* The cue time is the matched word's start plus the offset key.
* `occurrence` picks a later match. The default is the first.
* A cue id must not contain a comma, because a comma breaks `?cues=`. DeckTalk does not check this.
* An `@` inside an id still works. By convention, ids have no `@`.
* `align` ignores a top-level key other than `sections`, such as `_comment`.

Full rules: [cues.json](/reference/cues-json) and [Cues](/concepts/cues).

### Pages

* A page is any HTML file that a `[[section]]` names.
* The starter has one page, `deck/index.html`, with three scenes. A project can have as many pages as it likes.
* Load `decktalk-runtime.js` before any `DeckTalk.scene` call.
* A `<div data-scene="N">` holding one `<template data-slide="N.1">` per slide declares a scene in markup, and needs no JavaScript.
* `DeckTalk.scene(N, {name, camera?, slides})` declares the same scene in script, for a slide that builds itself in code.
* Each slide is `{id, hold?, owns?, preview?, render, enter?, on?}`.
* `enter`, each `on[id]`, and `DeckTalk.on(id, fn)` receive `(slide, {id, at, frozen, slideId})`.
* An element appears on its cue with `data-cue="id"`, or at a time with `data-delay="s"`.
* `data-text="spoken"`, `data-text="count"`, and `data-text="type"` also need `data-cue` or `data-delay`.
* Inside `render` backticks, write every backslash twice.
* The runtime creates the stage element `#dt-stage` at 1920 by 1080.
* A slide owns a cue whose id is its own, one its `owns` list names, or one whose longest slide-id prefix it is. A `preview` entry is timing only and grants no ownership.

Full rules: [Runtime](/reference/runtime) and [Page contract](/concepts/page-contract).

### `.env`

* A voiced build needs `ELEVENLABS_API_KEY` and `ELEVENLABS_VOICE_ID`.
* An exported variable wins over `.env`.
* A value that starts with `<` counts as unset.
* A build without voice needs neither key.

### One number for each section

* The number `N` is the same in the script heading, `[[section]]`, and the `cues.json` key.
* A section can play a scene with another number, and a cue id starts with the id of the slide that owns it rather than with the section number.
* `align` exits 1 on a cue id that its page never quotes.
* `build` stops on such an id unless you pass `--allow-unknown-cues`.

[Cue mismatches](/concepts/cues#cue-mismatches) lists every page warning about cues.

### A minimal page

This page is the smallest page that plays one scene. For KaTeX, keep the `deck/katex/` directory that
`decktalk init` wrote, and keep its two tags before the runtime.

```html deck/index.html theme={null}
<!doctype html>
<meta charset="utf-8">
<link rel="stylesheet" href="./katex/katex.min.css">
<script src="./katex/katex.min.js"></script>
<style>
  html, body, #dt-stage { margin: 0; background: #ffffff; color: #0a0a0a; }
  .slide { position: absolute; inset: 0; }
</style>
<script src="decktalk-runtime.js"></script>
<script>
DeckTalk.scene(1, { name: "Open", slides: [
  { id: "1.1", hold: 8, preview: { "1.1a": 1 }, render: () => `
    <div class="slide"><h1 data-cue="1.1a">Hello</h1></div>` },
]});
</script>
```

The runtime mounts each slide as a `.dt-slide` with `position: absolute; inset: 0`. Set your own
background on `html, body` and `#dt-stage`.

## Commands

```console theme={null}
uv tool install decktalk                    # Python 3.12 or later. pipx install decktalk also works.
decktalk install                            # once per machine: Chromium and ffmpeg
decktalk doctor                             # each tool and its path. Exit 1 on any MISSING row.
decktalk init DIR [--name NAME] [--force]   # write the starter, with KaTeX in deck/katex/
decktalk init DIR --example lesson          # a packaged example instead. --no-skills leaves the skills out.
decktalk build --no-voice                   # every stage with a click track. No key.
decktalk build                              # the voiced video at build/out/<name>.mp4
decktalk build --only 3 --preset veryfast   # record section 3 again, with a fast encode
decktalk align                              # the cue time of every cue. Exit 1 on a missing phrase or id.
decktalk verify                             # section starts, cuts, and every cue in the final video
decktalk verify 1:1.1title 2:2.1code        # only these two cues
decktalk verify --json                      # the same result as one JSON object on stdout
decktalk screenshots                        # one PNG per slide
decktalk screenshots --slide 2.1 --after 2.1formula           # slide 2.1 frozen at cue 2.1formula
decktalk screenshots --section 2 --at 12.5  # a frame 12.5 s after narration t=0 in section 2
decktalk words --only 1                     # section 1's words, in seconds after the section starts
decktalk clip 1 --start 2.3 --end 8.4 --out media/count.mp4   # that span of section 1 as a clip and a words file
decktalk status [--json]                    # what the four input files say, and what disagrees
decktalk narrate --dry-run                  # the text, and which sections a voiced run voices. Sends nothing.
decktalk narrate --dry-run --json           # the same take plan as one JSON object
decktalk preflight                          # takes, cues, and frozen reveals before a voiced build
decktalk soundscape --dry-run               # every sound request. Sends nothing.
decktalk serve                              # the project on the local origin, so a page loads over http
```

* A project command takes `-p DIR`. The default is `DECKTALK_PROJECT`, then the current directory.
* `-v` and `-q` go before or after the command name.
* `-p` goes before the command name, or after it on every command except `init`, `install`, and `doctor`.
* `--only` takes a number, a list or a range, and repeats: `--only 3`, `--only 3,5`, `--only 7-9`.
* `--strict` fails on an uncertain finding, and on `assemble` and `build` on a missing recording or clip.
* A clip section with `optional = true` still plays its slate under `--strict`.

Every command prints one JSON envelope with `--json`, and each takes the exit options its own
findings can reach.

| Option        | Commands                                                              | Meaning                                                                 |
| ------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `--json`      | every command                                                         | One envelope on stdout, and nothing else there. An error prints it too. |
| `--strict`    | `align`, `preflight`, `record`, `assemble`, `verify`, `clip`, `build` | Also exits 1 on an uncertain finding.                                   |
| `--exit-zero` | the same commands, plus `doctor` and `status`                         | Exits 0 on findings. An error still exits 3.                            |

| Exit | Meaning                                                      |
| ---- | ------------------------------------------------------------ |
| 0    | Nothing found, or `--exit-zero`.                             |
| 1    | A finding. `error` is null, and the project is what to fix.  |
| 2    | A usage error. `error.code` is `USAGE`.                      |
| 3    | DeckTalk could not run. `error` names the error class. Stop. |
| 130  | Interrupted.                                                 |

Every option: [CLI](/reference/cli).

## Gate a script on the video

* `build` ends with the same five checks `decktalk verify` runs, cues included.
* `build` exits 1 on any certain finding, so `TRUNCATED`, `STALLED` and `NO COVER` each fail a run.
* To gate a script on the video with no key, run `decktalk build --no-voice`.
* The offset column must be between -80 ms and +80 ms.
* After a build without voice, the `a/v` column must be between -120 ms and +120 ms.
* `OFF CUE` prints no distance. Compare the columns with those limits.
* A `-` in the `a/v` column means no click was found. The JSON reason is `NO_CLICK`, and it does not fail.
* Every cut must be at most -40 dBFS in its last 0.15 s. Clip sections have no cut row.
* A build without voice skips the loudness pass.

| `skipped` reason        | Cause                                                                                 |
| ----------------------- | ------------------------------------------------------------------------------------- |
| `OPTED_OUT`             | `"verify": false` in `cues.json`. Name the cue to measure it.                         |
| `REFERENCE_CLAMPED`     | The cue is too close to the section start. A `$start` cue with no offset key gets it. |
| `SECTION_NOT_ASSEMBLED` | `build/sections/NN.mp4` is missing.                                                   |
| `TOO_CLOSE_TO_END`      | The cue is less than 0.75 s before the section end.                                   |

A `skipped` row never fails. A named cue that is not in `cue-times.json` is `UNRESOLVED`, which fails.
Measurement details: [verify](/reference/verify).

To test a new project with no key, run these commands in order.

1. Run `decktalk build --no-voice`.
2. Run `decktalk verify`.
3. Run `decktalk screenshots`.

To see the cue times before anything records, run `decktalk narrate --no-voice` and then
`decktalk align`.

## Stages and artifacts

`build` runs `narrate`, `align`, `record`, `assemble` and `verify`, in that
order.

| Artifact                                          | Written by    | Holds                                                                                                                                                                            |
| ------------------------------------------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `build/narration/<hash>.mp3`, `<hash>.words.json` | `narrate`     | One take's audio and its `[{word, start, end}]` list, named by the take's content hash.                                                                                          |
| `build/narration/takes.json`                      | `narrate`     | One entry per section, with its text hash and lengths, which is also the narration clock.                                                                                        |
| `build/narration/narration.mp3`                   | `narrate`     | Spoken sections back to back, with each section's silence joined around its take.                                                                                                |
| `build/cue-times.json`                            | `align`       | One row per cue with `cue`, `on`, `at` and `word_at`, in seconds after the section start.                                                                                        |
| `build/recordings/NN.webm`, `recordings/NN.json`  | `record`      | The recording and its recording log: t=0, its checks, warnings, page errors, and frame gaps.                                                                                     |
| `build/sections/NN.mp4`                           | `assemble`    | One section cut to its span, video only.                                                                                                                                         |
| `build/out/<name>.mp4`                            | `assemble`    | The video with chapters, and both streams tagged with `[project] language`. A second copy named with the date and time is written only when `[output] timestamped_copy` is true. |
| `build/out/<name>.srt`, `<name>.vtt`              | `assemble`    | Captions from the word times.                                                                                                                                                    |
| `build/out/<name>.chapters.txt`                   | `assemble`    | One chapter per section, or per run of sections with the same chapter.                                                                                                           |
| `build/screenshots/**/*.png`                      | `screenshots` | Review screenshots, in one folder per page when there are several pages.                                                                                                         |

After a clip between page sections, final-video times are later than narration times by the clip
lengths before them. [Time bases](/reference/artifacts#time-bases) gives each clock.
[Build artifacts](/reference/artifacts) gives every field.

## Settings

Settings come from five layers. Each layer overrides the layer before it.

1. DeckTalk's defaults.
2. The per-machine file at `DECKTALK_CONFIG`. It can hold tuning tables only.
3. Tables of the same name in `decktalk.toml`.
4. `DECKTALK_<TABLE>_<FIELD>` environment variables.
5. The options `--preset`, `--crf`, `--settle`, and `--model`.

The tuning tables are these seven.

* `[video]`
* `[narration]`
* `[record]`
* `[audio]`
* `[verify]`
* `[elevenlabs]`
* `[output]`

`DECKTALK_CACHE_DIR` moves the ffmpeg cache. `DECKTALK_FFMPEG` and `DECKTALK_FFPROBE` point at your
own binaries. Set both. [CLI](/reference/cli#environment-variables) gives the default paths, and
[Configuration](/reference/configuration) lists every field.

## Exit codes and errors

| Code | Meaning                                                                                                                                 |
| ---- | --------------------------------------------------------------------------------------------------------------------------------------- |
| 0    | Nothing found, or `--exit-zero` on a command with findings.                                                                             |
| 1    | A finding. `error` is null, and the project is what to fix. [Exit codes](/reference/cli#exit-codes) lists the findings of each command. |
| 2    | A usage error, such as an unknown option or a missing command. `error.code` is `USAGE`.                                                 |
| 3    | DeckTalk itself could not run. `error` names the error class, so stop.                                                                  |
| 130  | Interrupted.                                                                                                                            |

| Error               | `error.code`    | Meaning                                |
| ------------------- | --------------- | -------------------------------------- |
| `ConfigError`       | `CONFIG`        | An input is bad or inconsistent.       |
| `MissingInputError` | `MISSING_INPUT` | An earlier stage has not run.          |
| `ProviderError`     | `PROVIDER`      | The speech provider refused or failed. |
| `ToolError`         | `TOOL`          | ffmpeg or Chromium failed.             |

An error prints `error[CODE]: <message>` on stderr and exits 3, even with `--exit-zero`. Under
`--json` it fills the envelope's `error` slot instead. Logs go to stderr through the `decktalk`
logger. DeckTalk never prints a key.

## Python

This example builds a project silently and prints the final path.

```python theme={null}
import decktalk
project = decktalk.Project.load("my-lesson")
result = decktalk.build(project, silent=True)
print(result.assembly.final, result.verification.ok)
```

[Python API](/reference/python-api) lists every public name.

## Related

* **Look up an option:** [CLI](/reference/cli)
* **Fix a message:** [Troubleshooting](/help/troubleshooting)
* **Look up a term:** [Glossary](/reference/glossary)
