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

# Your first deck

> Write one section in the four input files, then build it and verify its reveals.

This guide writes one section of a deck in the four files you write. Use it after the [quickstart](/quickstart), when you are ready to make a section of your own.

The section is a short lesson on gradient descent. Its slide draws a bowl in SVG and typesets the update rule with KaTeX.

## Before you start

Start from the starter, and add to it. Nothing has to be deleted first.

1. Create a project with `decktalk init my-lesson`. The starter puts the runtime and KaTeX in `deck/`.
2. Go into the project with `cd my-lesson`.

The starter has sections 1, 2 and 3 on `deck/index.html`, with one scene each. The steps below add a
section 4, with a scene of its own on the same page, after the starter's Close.

Declare each scene number only one time in a page. A second `[data-scene="4"]` wrapper replaces the
first, and no warning appears.

## How the four files connect

One section number and one cue id tie the four files together.

| In                | Name                                                       | Refers to                                                     |
| ----------------- | ---------------------------------------------------------- | ------------------------------------------------------------- |
| `script.md`       | `## 4.`                                                    | the section number                                            |
| `decktalk.toml`   | `number = 4`, `scene = 4`                                  | the same section, and the scene the page plays for it         |
| `cues.json`       | `"4"`, `"cue": "4.1rule"`                                  | the section, and a cue id that the page knows                 |
| `deck/index.html` | `data-scene="4"`, `data-slide="4.1"`, `data-cue="4.1rule"` | the scene, its slide, and the element that appears on the cue |

The `narrate` stage also writes a words file for the section, with the start and end of every spoken word. The `align` stage matches each cue phrase against those spoken words.

## Write the section

<Steps>
  <Step title="Add the script to script.md">
    Paste this section into `script.md`, after section 3.

    ```md script.md theme={null}
    ## 4. Gradient descent

    [Deck scene 4. A loss bowl draws, a point steps down it, and the update rule appears.]

    Now a short lesson. Here is gradient descent, at a high level. [beat] Start with a loss
    surface. For this lesson it is a bowl. [beat] Pick a point anywhere on it. [beat] The gradient
    says which way is uphill, so take a step the other way. [beat] The learning rate sets
    the size of that step. [beat] Step again. [beat] And again, and each step is smaller as
    the slope flattens out.

    [beat] Pause and think: what happens if the learning rate is too large?

    [pause 3]

    You overshoot, and bounce from side to side. [beat] Too small, and you crawl. [beat] Just
    right, and you settle at the bottom.
    ```

    Each `## N. Title` heading starts a section. The voice does not speak text in square brackets. `[beat]` makes a short pause, and `[pause 3]` asks the voice for three seconds of silence. [script.md](/reference/script-md) lists every rule.
  </Step>

  <Step title="Add the section to decktalk.toml">
    Paste this table after the section 3 table.

    ```toml decktalk.toml theme={null}
    [[section]]
    number = 4
    chapter = "Gradient descent"
    page = "deck/index.html"
    scene = 4
    ```

    Every script section needs one `[[section]]` table with the same `number`. `page` names the page, and `scene` names the scene that the page plays. [decktalk.toml](/reference/decktalk-toml) lists every key.
  </Step>

  <Step title="Add the cues to cues.json">
    Add this entry under `"sections"`.

    ```json cues.json theme={null}
    "4": {
      "min_seconds": 30,
      "cues": [
        { "cue": "4.1", "on": "$start" },
        { "cue": "4.1bowl", "on": "loss surface" },
        { "cue": "4.1p0", "on": "Pick a point" },
        { "cue": "4.1p1", "on": "take a step" },
        { "cue": "4.1rule", "on": "learning rate", "occurrence": 1 },
        { "cue": "4.1p2", "on": "Step again" },
        { "cue": "4.1p3", "on": "And again" },
        { "cue": "4.1think", "on": "Pause and think" },
        { "cue": "4.1over", "on": "overshoot" },
        { "cue": "4.1settle", "on": "settle", "offset": 0.2 }
      ]
    }
    ```

    Each reveal starts on one phrase:

    * The bowl draws on "loss surface".
    * Each move of the point starts on the phrase that names it, such as "take a step".
    * The update rule appears on "learning rate", and names its occurrence because the section
      says the phrase twice.
    * The overshoot path draws on "overshoot".
    * The settled point appears 0.2 s after the start of "settle", because its offset key is 0.2.

    The `4.1` cue on `$start` only mounts the slide at 0.0 s, so both checks skip it: `decktalk preflight` as `NO_SLIDE`, because the slide declares no element for it, and `decktalk verify` as `REFERENCE_CLAMPED`, because no frame comes before 0.0 s. Neither skip fails. If the speech is shorter than `min_seconds`, `decktalk align` reports an uncertain finding. [Cues](/concepts/cues) explains how phrases match.
  </Step>

  <Step title="Add the scene to deck/index.html">
    Paste the CSS into the page's `<style>` element, and the scene just before `</body>` in `deck/index.html`.

    <Tip>Markup in a `<template data-slide>` is not a JavaScript string, so a backslash is written once, and
    `${` and a backtick are ordinary characters. That is the reason to write a slide as markup rather than as a
    [`render`](/reference/runtime#slide-fields) function.</Tip>

    ```html deck/index.html theme={null}
    <style>
      .row { position: absolute; left: 120px; top: 200px; right: 120px; display: flex; gap: 64px; align-items: flex-start; }
      .row .text { flex: 1; }
      .h2 { margin: 0 0 16px; font: 600 64px/1.1 var(--sans); letter-spacing: -.02em; color: var(--ink); }
      .note { margin: 0; font-size: 36px; line-height: 1.3; color: var(--mute); }
      .math { margin: 40px 0; font-size: 58px; color: var(--ink); }
      .ax { stroke: var(--mute); stroke-width: 2; }
      .curve { fill: none; stroke: var(--accent); stroke-width: 6; stroke-linecap: round; }
      .zig { fill: none; stroke: var(--mute); stroke-width: 4; stroke-dasharray: 12 10; }
      .step circle { fill: var(--accent); }
      .step line { stroke: var(--ink); stroke-width: 4; }
      .step .ring { fill: none; stroke: var(--accent); stroke-width: 3; }
    </style>
    <div data-scene="4" data-name="Gradient descent">
      <template data-slide="4.1" data-hold="26"
                data-preview="4.1bowl@0.5 4.1p0@3 4.1p1@5 4.1rule@7 4.1p2@9 4.1p3@11 4.1think@13 4.1over@17 4.1settle@21">
        <div class="row">
          <svg viewBox="0 0 1000 520" width="1000" height="520" style="overflow:visible">
            <line class="ax" x1="60" y1="470" x2="960" y2="470"/>
            <line class="ax" x1="60" y1="40" x2="60" y2="470"/>
            <path class="curve" pathLength="1" data-cue="4.1bowl" data-reveal="draw" data-duration="1.6"
                  data-describe="a loss bowl draws from left to right" d="M80,70 Q510,880 940,70"/>
            <path class="zig" pathLength="1" data-cue="4.1over" data-reveal="draw" data-duration="1.4"
                  data-describe="a zigzag path bounces from side to side" d="M190,200 L830,200 L230,320 L790,320 L300,410"/>
            <g class="step" data-cue="4.1p0" data-reveal="pop"><circle cx="190" cy="200" r="26"/></g>
            <g class="step" data-cue="4.1p1" data-reveal="pop"><line x1="190" y1="200" x2="330" y2="330"/><circle cx="330" cy="330" r="26"/></g>
            <g class="step" data-cue="4.1p2" data-reveal="pop"><line x1="330" y1="330" x2="420" y2="405"/><circle cx="420" cy="405" r="26"/></g>
            <g class="step" data-cue="4.1p3" data-reveal="pop"><line x1="420" y1="405" x2="470" y2="440"/><circle cx="470" cy="440" r="26"/></g>
            <g class="step" data-cue="4.1settle" data-reveal="pop"><circle cx="510" cy="453" r="34"/><circle class="ring" cx="510" cy="453" r="52"/></g>
          </svg>
          <div class="text">
            <h2 class="h2">Gradient descent</h2>
            <p class="note">Follow the slope downhill, one step at a time.</p>
            <div class="math" data-cue="4.1rule" data-tex-display data-tex="\theta_{t+1} = \theta_t - \eta\,\nabla L(\theta_t)"
                 data-describe="the update rule for gradient descent">θ(t+1) = θ(t) − η ∇L(θ(t))</div>
            <p class="note" data-cue="4.1think" data-reveal="fade">Pause and think: what if the learning rate is too large?</p>
          </div>
        </div>
      </template>
    </div>
    ```

    * A `[data-scene]` wrapper is a scene, and each `<template data-slide>` inside it is one slide. The wrapper never draws.
    * Slide `4.1` owns every cue here, because every cue id starts with the slide id. `data-preview` says only when each one fires in a browser preview.
    * An element with `data-cue` stays hidden until its cue fires. `data-reveal` picks its reveal effect, and `data-describe` says in a sentence what the reveal shows.
    * The bowl path has `pathLength="1"` and `data-reveal="draw"`, so its stroke draws over `data-duration` (1.6 s).
    * KaTeX typesets the `data-tex` element. The plain text inside the element is the fallback.

    The scene brings its own CSS, because the starter's stylesheet styles only its own classes. [Runtime](/reference/runtime) lists every attribute.
  </Step>
</Steps>

## Look before you build

Check the slide in a browser and in screenshots before you record it. [Browser preview and recording](/concepts/page-contract#browser-preview-and-recording) says which page values a browser preview uses and which values the recording uses.

1. Run `decktalk serve` and follow the URL it prints. That is the origin the recorder uses.
2. Open `deck/index.html?slide=4.1` there. The slide mounts with every element visible.
3. Open `deck/index.html?scene=4&hud=1`. The scene plays in preview, with the clock on top.
4. Take a screenshot of every slide with `decktalk screenshots`. The PNG files go under `build/screenshots/`.
5. Estimate every reveal with `decktalk preflight --only 4`. It freezes the page before and at each cue, and flags a
   reveal too small for `verify` as `THIN CHANGE?` or `NO CHANGE`.

```text theme={null}
check              slide       cue   chg %  result
4:4.1              4.1        0.00       -  skipped NO_SLIDE: slide 4.1 declares no element for the cue
4:4.1bowl          4.1        7.89    0.49  changed
4:4.1p0            4.1       12.33    0.11  THIN CHANGE?
4:4.1p1            4.1       19.23    0.16  THIN CHANGE?
4:4.1rule          4.1       22.68    0.45  changed
4:4.1p2            4.1       26.63    0.14  THIN CHANGE?
4:4.1p3            4.1       27.61    0.11  THIN CHANGE?
4:4.1think         4.1       33.53    0.45  changed
4:4.1over          4.1       39.94    0.73  changed
4:4.1settle        4.1       48.03    0.22  THIN CHANGE?
10 cue(s): 4 changed, 5 THIN CHANGE?, 1 skipped. Frozen frames in build/preflight
```

The five point moves and the settled dot change 0.11 to 0.22 percent of the frame, over the 0.1 percent floor that
would read `NO CHANGE` and under the 0.3 percent one that reads `changed`, so `preflight` calls each of them
`THIN CHANGE?`. A larger dot clears the floor. [Design a slide](/guides/design-a-slide#make-each-reveal-big-enough-to-measure)
gives the pixel sizes.

If the project has no voiced build yet, you can also check where each cue phrase resolves. On a voiced project, a `narrate` without voice stops, because it would empty the narration cache. Skip these actions there.

1. Write estimated word times with `decktalk narrate --no-voice`.
2. Print each cue time with `decktalk align`.

```text theme={null}
sec  speech   need  cues
 04    49.8   30.0  4.1@0.0,4.1bowl@7.89,4.1p0@12.33,4.1p1@19.23,4.1rule@22.68,4.1p2@26.63,4.1p3@27.61,4.1think@33.53,4.1over@39.94,4.1settle@48.03
```

Each entry reads `cue id@cue time`, in seconds after the section start. If a phrase is not in the spoken words, `align` prints `phrase not found`, and `build` stops.

## Build and verify

Build the video without an account, then measure the reveals.

1. Build the video with `decktalk build --no-voice`.
2. Measure two cues with `decktalk verify 4:4.1rule 4:4.1over`.

```text theme={null}
check                 cue       at   chg %   ctl %   offset     a/v  result
4:4.1rule           22.68    67.56    0.46    0.00    -40ms   -49ms  changed
4:4.1over           39.94    84.82    0.72    0.00    -60ms   -64ms  changed
```

Both rows read `changed`. The offset column is the time from the cue time to the onset of the reveal, in milliseconds. `decktalk verify` reports `OFF CUE` and exits 1 when that time is outside the offset limit. Only a build without voice gives the `a/v` column, because only the click track has clicks. [Verify](/reference/verify#cues) defines every column.

Run `decktalk verify` with no arguments to check every cue of every section, the section starts and the cuts at once.

If you have an ElevenLabs key in `.env`, run `decktalk build` to voice the video. It spends credits on every section, because the last build had no voice.

## The finished section

These are the four files of the section, in one place.

<CodeGroup>
  ```md script.md theme={null}
  ## 4. Gradient descent

  [Deck scene 4. A loss bowl draws, a point steps down it, and the update rule appears.]

  Now a short lesson. Here is gradient descent, at a high level. [beat] Start with a loss
  surface. For this lesson it is a bowl. [beat] Pick a point anywhere on it. [beat] The gradient
  says which way is uphill, so take a step the other way. [beat] The learning rate sets
  the size of that step. [beat] Step again. [beat] And again, and each step is smaller as
  the slope flattens out.

  [beat] Pause and think: what happens if the learning rate is too large?

  [pause 3]

  You overshoot, and bounce from side to side. [beat] Too small, and you crawl. [beat] Just
  right, and you settle at the bottom.
  ```

  ```toml decktalk.toml theme={null}
  [[section]]
  number = 4
  chapter = "Gradient descent"
  page = "deck/index.html"
  scene = 4
  ```

  ```json cues.json theme={null}
  "4": {
    "min_seconds": 30,
    "cues": [
      { "cue": "4.1", "on": "$start" },
      { "cue": "4.1bowl", "on": "loss surface" },
      { "cue": "4.1p0", "on": "Pick a point" },
      { "cue": "4.1p1", "on": "take a step" },
      { "cue": "4.1rule", "on": "learning rate", "occurrence": 1 },
      { "cue": "4.1p2", "on": "Step again" },
      { "cue": "4.1p3", "on": "And again" },
      { "cue": "4.1think", "on": "Pause and think" },
      { "cue": "4.1over", "on": "overshoot" },
      { "cue": "4.1settle", "on": "settle", "offset": 0.2 }
    ]
  }
  ```

  ```html deck/index.html theme={null}
  <style>
    .row { position: absolute; left: 120px; top: 200px; right: 120px; display: flex; gap: 64px; align-items: flex-start; }
    .row .text { flex: 1; }
    .h2 { margin: 0 0 16px; font: 600 64px/1.1 var(--sans); letter-spacing: -.02em; color: var(--ink); }
    .note { margin: 0; font-size: 36px; line-height: 1.3; color: var(--mute); }
    .math { margin: 40px 0; font-size: 58px; color: var(--ink); }
    .ax { stroke: var(--mute); stroke-width: 2; }
    .curve { fill: none; stroke: var(--accent); stroke-width: 6; stroke-linecap: round; }
    .zig { fill: none; stroke: var(--mute); stroke-width: 4; stroke-dasharray: 12 10; }
    .step circle { fill: var(--accent); }
    .step line { stroke: var(--ink); stroke-width: 4; }
    .step .ring { fill: none; stroke: var(--accent); stroke-width: 3; }
  </style>
  <div data-scene="4" data-name="Gradient descent">
    <template data-slide="4.1" data-hold="26"
              data-preview="4.1bowl@0.5 4.1p0@3 4.1p1@5 4.1rule@7 4.1p2@9 4.1p3@11 4.1think@13 4.1over@17 4.1settle@21">
      <div class="row">
        <svg viewBox="0 0 1000 520" width="1000" height="520" style="overflow:visible">
          <line class="ax" x1="60" y1="470" x2="960" y2="470"/>
          <line class="ax" x1="60" y1="40" x2="60" y2="470"/>
          <path class="curve" pathLength="1" data-cue="4.1bowl" data-reveal="draw" data-duration="1.6"
                data-describe="a loss bowl draws from left to right" d="M80,70 Q510,880 940,70"/>
          <path class="zig" pathLength="1" data-cue="4.1over" data-reveal="draw" data-duration="1.4"
                data-describe="a zigzag path bounces from side to side" d="M190,200 L830,200 L230,320 L790,320 L300,410"/>
          <g class="step" data-cue="4.1p0" data-reveal="pop"><circle cx="190" cy="200" r="26"/></g>
          <g class="step" data-cue="4.1p1" data-reveal="pop"><line x1="190" y1="200" x2="330" y2="330"/><circle cx="330" cy="330" r="26"/></g>
          <g class="step" data-cue="4.1p2" data-reveal="pop"><line x1="330" y1="330" x2="420" y2="405"/><circle cx="420" cy="405" r="26"/></g>
          <g class="step" data-cue="4.1p3" data-reveal="pop"><line x1="420" y1="405" x2="470" y2="440"/><circle cx="470" cy="440" r="26"/></g>
          <g class="step" data-cue="4.1settle" data-reveal="pop"><circle cx="510" cy="453" r="34"/><circle class="ring" cx="510" cy="453" r="52"/></g>
        </svg>
        <div class="text">
          <h2 class="h2">Gradient descent</h2>
          <p class="note">Follow the slope downhill, one step at a time.</p>
          <div class="math" data-cue="4.1rule" data-tex-display data-tex="\theta_{t+1} = \theta_t - \eta\,\nabla L(\theta_t)"
               data-describe="the update rule for gradient descent">θ(t+1) = θ(t) − η ∇L(θ(t))</div>
          <p class="note" data-cue="4.1think" data-reveal="fade">Pause and think: what if the learning rate is too large?</p>
        </div>
      </div>
    </template>
  </div>
  ```
</CodeGroup>

## Next

* **Copy a common slide:** [Slide recipes](/guides/slide-recipes)
* **Make the script time well:** [Writing for the ear](/guides/writing-for-the-ear)
* **Open the video with a clip:** [Add a clip section](/guides/clip-section)
