> ## 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.

# How it works

> Understand how DeckTalk turns a script and HTML pages into one mp4, and why each reveal starts on its word.

`decktalk build` turns your script and pages into one mp4 in five stages. To build your first video, follow the
[Quickstart](/quickstart).

<Frame caption="Each panel names the pipeline stages it runs, in order.">
  <picture className="block dark:hidden">
    <source media="(max-width: 640px)" />

    <img src="https://mintcdn.com/decktalk/knAnjfR4WMnCHJIJ/images/how-it-works-light.svg?fit=max&auto=format&n=knAnjfR4WMnCHJIJ&q=85&s=9cb4ca115333cf7fb0622ab2057a1865" alt="Four panels. Write shows a markdown script. Narrate runs narrate and align, and shows a tick for every word of &#x22;A bowl. A ball. One. Two, three.&#x22; with 1.25 over &#x22;bowl&#x22;. Record runs record, and shows a slide where a bowl draws on and a ball steps down it. Assemble runs assemble and verify, and shows one mp4." width="1200" height="262" data-path="images/how-it-works-light.svg" />
  </picture>

  <picture className="hidden dark:block">
    <source media="(max-width: 640px)" />

    <img src="https://mintcdn.com/decktalk/knAnjfR4WMnCHJIJ/images/how-it-works-dark.svg?fit=max&auto=format&n=knAnjfR4WMnCHJIJ&q=85&s=2d5ead12f9b8ef51ab69f4ed113b6f28" alt="Four panels. Write shows a markdown script. Narrate runs narrate and align, and shows a tick for every word of &#x22;A bowl. A ball. One. Two, three.&#x22; with 1.25 over &#x22;bowl&#x22;. Record runs record, and shows a slide where a bowl draws on and a ball steps down it. Assemble runs assemble and verify, and shows one mp4." width="1200" height="262" data-path="images/how-it-works-dark.svg" />
  </picture>
</Frame>

1. You write four files: `script.md`, `decktalk.toml`, `cues.json`, and the page.
2. The speech provider voices the script and returns a start and an end time for every word.
3. Headless Chromium records each page section, and each reveal fires at its cue time.
4. ffmpeg cuts each recording to its section and joins the sections into one mp4.
5. `verify` measures the final mp4, so an early or late reveal shows as a number.

## Stages

The five stages always run in this order. Each stage reads files that the stages before it wrote.

```mermaid theme={null}
flowchart TB
  S["script.md"] --> narrate
  narrate -- "words files, takes.json" --> align
  C["cues.json"] --> align
  align -- "cue-times.json" --> record
  P["deck pages"] --> record
  record -- "webm and recording log" --> assemble
  assemble -- "name.mp4, captions, chapters" --> verify
```

| Stage      | One job                                                                                                                                                      | Writes                                                                           |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------- |
| `narrate`  | `narrate` voices each section and gets a time for every word.                                                                                                | `build/narration/<hash>.mp3`, `<hash>.words.json`, `takes.json`, `narration.mp3` |
| `align`    | `align` turns each cue phrase into a cue time.                                                                                                               | `build/cue-times.json`                                                           |
| `record`   | `record` records each page section in headless Chromium, finds narration t=0 in the webm, and flags a recording that is black, short, stalled, or uncovered. | `build/recordings/NN.webm`, `recordings/NN.json`                                 |
| `assemble` | `assemble` cuts, joins, and mixes the sections into one mp4.                                                                                                 | `build/sections/NN.mp4`, `<name>.mp4`, captions, chapters                        |
| `verify`   | `verify` checks section starts, cuts, and cut continuity in the final mp4.                                                                                   | nothing                                                                          |

Each stage is also a command with the same name, and [the CLI reference](/reference/cli) lists them.
[Build artifacts](/reference/artifacts) gives the fields of every file. `assemble` also runs the
[loudness pass](/concepts/sound#loudness).

## Why the cuts are exact

Each reveal starts on its word because DeckTalk finds narration t=0 inside every recording, to the frame.

<Frame caption="The bowl and the ball each appear whole in the first frame after their words.">
  <img className="block dark:hidden" src="https://mintcdn.com/decktalk/xS__ZpzOkvCjh1FH/images/alignment-light.svg?fit=max&auto=format&n=xS__ZpzOkvCjh1FH&q=85&s=cece3f26b3b617c0c2497099969f0518" alt="A strip of recorded frames. Three magenta cover frames come first, and a line marks t=0 at the first clean frame. Under the strip, the narration &#x22;A bowl. A ball. Watch it step down&#x22; starts at t=0. Dashed leads join &#x22;bowl&#x22; and &#x22;ball&#x22; to the outlined frames where the bowl and then the ball appear." width="1200" height="250" data-path="images/alignment-light.svg" />

  <img className="hidden dark:block" src="https://mintcdn.com/decktalk/xS__ZpzOkvCjh1FH/images/alignment-dark.svg?fit=max&auto=format&n=xS__ZpzOkvCjh1FH&q=85&s=7453f40ca56a8c6685def4041a74e8c2" alt="A strip of recorded frames. Three magenta cover frames come first, and a line marks t=0 at the first clean frame. Under the strip, the narration &#x22;A bowl. A ball. Watch it step down&#x22; starts at t=0. Dashed leads join &#x22;bowl&#x22; and &#x22;ball&#x22; to the outlined frames where the bowl and then the ball appear." width="1200" height="250" data-path="images/alignment-dark.svg" />
</Frame>

| Risk                                                                                             | What DeckTalk does                                                                                  |
| ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- |
| A browser starts to record at an unknown moment. Chromium on Windows starts later than on Linux. | The recorder covers the page in magenta and takes the first frame after the cover as narration t=0. |
| Chromium sends a frame only when the page paints.                                                | The cover holds an element that always moves, so frames keep coming while the page is covered.      |
| t=0 is known only to the nearest frame.                                                          | The video runs at 25 fps, the rate that Chromium records at. t=0 is exact to one frame, 40 ms.      |
| Section lengths add up to fractions of a frame.                                                  | `assemble` rounds each section span to whole frames counted from the start of the narration.        |
| A recording can end before its section span.                                                     | `assemble` repeats the last frame to fill the span. `hold_seconds` uses the same repeat.            |
| Joined audio adds a short delay at each join.                                                    | Each `sections/NN.mp4` has no audio. `assemble` mixes the sound once, from zero.                    |

The recorder removes the cover only when the page is ready. [The handshake](/concepts/page-contract#the-handshake)
shows each wait. `record` looks for the cover only in the first `[record] cover_scan_seconds` of each recording, 4 s by
default. The alignment never depends on wall-clock time.

## The build without voice

A build without voice replaces the voice with a click track, so it needs no account and no API key.

```text theme={null}
[sil ] 01  26 words -> 12.60s (estimated words, sound ends 12.487, lead 0.5s, tail 1.3s)
[sil ] 02  30 words -> 14.20s (estimated words, sound ends 14.087, lead 0.5s, tail 1.3s)
[sil ] 03  25 words -> 11.50s (estimated words, sound ends 11.387, lead 0.5s, tail 1.3s)
```

* The click track has a soft click at the start of every estimated word, and one where the last word ends, at -24 dBFS.
* `narrate` spaces the estimated words evenly at `silent_words_per_minute`, 150 by default. It adds each pause,
  beat, and tail. [script.md](/reference/script-md) gives the length rule.
* Every other stage runs as usual. A build without voice therefore tests the cues, the pages, the recording, the cut, and
  the checks.
* The `narrate` and `align` tables mark their times as estimated.
* The clicks let `verify` compare the picture with the sound in the final mp4. [Verify](/reference/verify#cues)
  explains the check.
* A build without voice skips the loudness pass, so the clicks keep their level.

A build without voice stops when the project has voiced takes, because it would empty the narration cache.
[A build without voice and a voiced build](/guides/rebuild-one-section#a-build-without-voice-and-a-voiced-build) explains the cost.

## Clips and the narration

A clip section plays a video with its own audio. `narrate` and `record` skip it, and the narration
pauses for a clip between two page sections.

<Frame caption="The narration pauses for each clip and resumes with the next page section.">
  <img className="block dark:hidden" src="https://mintcdn.com/decktalk/knAnjfR4WMnCHJIJ/images/narration-split-light.svg?fit=max&auto=format&n=knAnjfR4WMnCHJIJ&q=85&s=800fcbedba9e443ee5d6714e91cf3f88" alt="Lanes on one time axis. The narration.mp3 lane holds sections 1, 2, 3, 4, 6, 8, and 9 back to back, with a split before sections 6 and 8. The video lane plays the 4 s clip of section 5 after section 4 and the 5.04 s slate of section 7 after section 6, and dashed leads move sections 6, 8, and 9 later. Captions sit under page sections only. Chapters mark the start of each title, and the five sections titled The edit share one." width="1200" height="340" data-path="images/narration-split-light.svg" />

  <img className="hidden dark:block" src="https://mintcdn.com/decktalk/knAnjfR4WMnCHJIJ/images/narration-split-dark.svg?fit=max&auto=format&n=knAnjfR4WMnCHJIJ&q=85&s=04e63711daeec489770cd5d098eaf6c5" alt="Lanes on one time axis. The narration.mp3 lane holds sections 1, 2, 3, 4, 6, 8, and 9 back to back, with a split before sections 6 and 8. The video lane plays the 4 s clip of section 5 after section 4 and the 5.04 s slate of section 7 after section 6, and dashed leads move sections 6, 8, and 9 later. Captions sit under page sections only. Chapters mark the start of each title, and the five sections titled The edit share one." width="1200" height="340" data-path="images/narration-split-dark.svg" />
</Frame>

[Add a clip section](/guides/clip-section) shows how to place a clip and how the narration splits.

## Caching

`narrate` keeps each section's audio and a hash of its text in `build/narration/takes.json`. After an edit,
`decktalk build` voices again only the sections whose text changed.

`record` keeps each section's recording the same way. The recording log holds `input_hash`, the digest of the
page URL with its cues and its spoken words, the frame geometry, the markup of the one scene the section plays,
the rest of that page, which every scene shares, and the content of every project file the page loaded, which
the log lists under `assets`. A section whose hash has not moved is kept. So an edit inside one scene records
the sections that play it and leaves the other sections of that page alone, while an edit to the page's head,
its styles or its scripts records every section of it. Replacing a picture the page loads records that section
again, although no line of HTML changed.
`decktalk build --only N` records section N whether it changed or not. A recording stays valid when a neighbor
section changes length, because cue times count from the start of their own section.
[Rebuild one section](/guides/rebuild-one-section) says what each edit runs again.

## What verify checks

`verify` checks the final mp4 in three ways.

* **Section starts:** the frame 0.2 s after each section start has a pixel brighter than luma 60.
* **Cuts:** the narration is at or below -40 dBFS in the last 0.15 s before each cut.
* **Cues:** the picture changes within 80 ms of each cue time. After a build without voice, it also changes within 120 ms
  of the click plus the offset key.

The verify stage inside `decktalk build` checks section starts and cuts, and cut continuity for a section that sets `seamless`. `decktalk verify` also checks the cues.
[Verify](/reference/verify) defines every measurement, limit, and result.

## Terms

| Term                                       | Meaning                                                                                                              |
| ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- |
| [Stage](/reference/glossary)               | One of `narrate`, `align`, `record`, `assemble`, and `verify`, in that order.                                        |
| [Build without voice](/reference/glossary) | A build with `--no-voice`. It uses estimated word times and writes a click track.                                    |
| [Voiced build](/reference/glossary)        | A build without `--no-voice`. The speech provider voices the script.                                                 |
| [Click track](/reference/glossary)         | The audio of a build without voice, with a soft click at every estimated word start and at the end of the last word. |
| [Narration t=0](/reference/glossary)       | The first frame after the magenta cover, where the page clock starts.                                                |
| [Recording](/reference/glossary)           | The webm that `record` writes for a page section.                                                                    |
| [Cut](/reference/glossary)                 | The boundary between two sections in the final video.                                                                |

## Next

* **Build your first video:** [Quickstart](/quickstart)
* **Rebuild after an edit:** [Rebuild one section](/guides/rebuild-one-section)
* **Read a verify table:** [Verify](/reference/verify)
