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

# Cues

> Learn how a cue phrase in cues.json becomes a cue time, and what happens when a cue and its page disagree.

A cue connects a spoken phrase to something that happens on a page, such as an element that
appears. Read this page when you choose a phrase, or when a cue does not resolve or starts on the
wrong word. [Writing for the ear](/guides/writing-for-the-ear) is the guide for writing phrases.

The starter's section 1 shows one cue, `1.1script`, in `script.md`, `cues.json`, and `deck/index.html`.

<CodeGroup>
  ```md script.md theme={null}
  ## 1. Open

  This is DeckTalk. [beat] You write what you will say, [beat] and you write the slides that go with it.
  [beat] Then every picture waits for its own word.
  ```

  ```json cues.json theme={null}
  { "sections": { "1": { "cues": [
    { "cue": "1.1title", "on": "This is DeckTalk", "occurrence": 1 },
    { "cue": "1.1script", "on": "what you will say", "occurrence": 1 },
    { "cue": "1.1deck", "on": "the slides", "occurrence": 1 },
    { "cue": "1.1word", "on": "its own word", "occurrence": 1 }
  ] } } }
  ```

  ```html deck/index.html theme={null}
  <div class="card" style="left: 120px; top: 430px" data-cue="1.1script"
       data-describe="a card for script.md, the file that holds what you say">
    <div class="k">script.md</div>
    <div class="v">what you will say</div>
  </div>
  ```
</CodeGroup>

1. You write the cue phrase in `cues.json`, and the cue id in the page.
2. The `align` stage resolves each cue phrase to a cue time, in seconds after the section start. The cue time of `1.1script` is the start of the word "what".
3. The `align` stage writes the cue times to `build/cue-times.json`.
4. The recorder passes the cue times to the page as `?cues=`, and the page fires each cue at its time.
5. `decktalk verify` checks that each reveal starts on its word.

`decktalk align` prints the cue times of every section. These lines come from a build without voice of
the starter, so the times are estimates.

```text theme={null}
sec  speech   need  cues
 01    13.0      -  1.1title@0.5,1.1script@2.9,1.1deck@6.27,1.1word@11.56
 02    14.6      -  2.1average@1.44,2.1formula@5.67,2.1code@9.43,2.1waited@11.31
 03    11.9      -  3.1idea@1.41,3.1again@7.8,3.1make@8.71
3 sections with cues, 0 unresolved  (estimated words: times are placeholders)
```

## Matching rules

The `align` stage matches a cue phrase against the section's spoken words with these rules:

1. It splits the phrase and the spoken words on whitespace only.
2. Each token keeps only A to Z, a to z, 0 to 9, and the straight apostrophe. Accented letters, Greek letters, and curly apostrophes are removed.
3. Matching ignores case, unless `case_sensitive` is true.
4. A phrase token with no characters left is ignored. For example, the phrase `learning rate η` matches as `learning rate`.
5. A hyphenated word is one token. "zig-zag" becomes `zigzag`, so the phrase "zig zag" does not match it.
6. The stage looks through the spoken words, in order, for a run that matches the phrase token for token. `occurrence` picks the run, and the default is the first. When the phrase matches more than once and the cue does not set `occurrence`, the stage warns and names each match with its time.
7. The cue time is the start of the run's first word, plus the offset key, rounded to hundredths of a second.
8. `$start` is 0.0 s plus the offset key. A negative result stays negative, and the page fires it at t=0.
9. `$end` is the end of the section's last spoken word, plus the offset key.

The page matches `data-text="spoken"` text with a different rule. It ignores apostrophes, but the `align`
stage keeps them. [`data-text="spoken"`](/reference/runtime#data-text="spoken") has the page's rules.

Write a cue phrase the way the voice says it, because the spoken words come from the voice. The
starter's script says "decktalk dot AI", and its end card shows decktalk.ai. So the cue `3.1make`
matches on "Make your own" and never on the text of the card. A number written in digits comes back
in the form the voice chose, so avoid digits in a cue phrase.

Stage directions are not spoken words. A `[beat]` becomes a dash, and a `[pause N]` becomes a break
tag. Neither is a word, so a phrase that spans a direction still matches.
[script.md](/reference/script-md) has the rules for directions.

## Which sections have cues

Every section key in `cues.json` must name a `[[section]]` in `decktalk.toml`. A section can have no
cues. A page section with no cues plays its preview timing in the recording. A clip section has no
narration, so the `align` stage skips its cues.

## Cue mismatches

A cue can disagree with its page in six ways. The `align` stage catches the first one before the
recording. The page catches the others during the recording.

| Mismatch                                                               | Caught by                       | Message                                                                                  | Result                                                                                                   |
| ---------------------------------------------------------------------- | ------------------------------- | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| A cue id in `cues.json` appears nowhere in its page.                   | the `align` stage               | `<id>: not in deck/index.html`                                                           | `align` exits 1, and `build` stops after the `align` table.                                              |
| No slide of the playing scene owns the cue.                            | the page, at the start          | `unknown cue id <id> (no slide id, owns list or preview object matches it)`              | The cue fires but mounts no slide. A matching element appears only if its slide is mounted at that time. |
| A slide owns the cue, but no element, handler, or slide id matches it. | the page, when the cue fires    | `cue "<id>" matches no element, handler, or slide`                                       | The cue fires with no visible effect. It still counts toward the mount time of its slide.                |
| An element's `data-cue` is not in `?cues=`.                            | the page, when the slide mounts | `data-cue "<id>" is not in ?cues=, so it reveals at its data-delay time after the mount` | The element appears at its `data-delay` time after the slide mounts.                                     |
| A slide owns no cue in `?cues=`.                                       | the page, at the start          | `slide "<slide>" owns no cue in ?cues=, so it never appears`                             | The slide never mounts.                                                                                  |
| No cue in `?cues=` has an owner.                                       | the page, at the start          | `no slide owns any listed cue, so nothing will mount`                                    | No slide mounts, and the recording shows an empty stage element.                                         |

In each message, `<id>` and `<slide>` stand for real ids. The page lists its warnings in
`window.__decktalk.warnings`. The recorder logs each one and writes it to the recording log. A handler that
`DeckTalk.on` registers for an id stops the second and third warnings for that id.
[Cue ownership](/concepts/page-contract#cue-ownership) has the ownership rules.

The `align` stage looks for each cue id in the page as a quoted literal, in single quotes, double
quotes, or backticks. So `data-cue="1.1bowl"`, a key of a slide's `preview` object, and a key of an `on`
object all count. The stage skips clip sections, and it skips a page file that does not exist,
because the recorder reports that. The cue still resolves, and `align` still writes `cue-times.json`.

These lines come from a copy of the starter whose `data-cue="2.1code"` was renamed `2.1codex` in
`deck/index.html`. `align` exits 1.

```text theme={null}
sec  speech   need  cues
 02    14.6      -  2.1average@1.44,2.1formula@5.67,2.1code@9.43,2.1waited@11.31
                    ! 2.1code: not in deck/index.html
                    ! 2.1codex: data-cue="2.1codex" in deck/index.html has no entry in cues.json
3 sections with cues, 0 unresolved  (estimated words: times are placeholders)
```

To fix an unknown id, add it to the page, or fix the id in `cues.json`. If a page builds its ids at
run time, pass `--allow-unknown-cues` to `align`, `preflight`, or `build`. The page warnings are then
the only check.

## Elements nobody cued

The check runs the other way too, because a cue and the element it reveals are one thing written in two
files. `align` reads every `data-cue` attribute in the pages and reports each id that `cues.json` never
names, as `UNCUED ELEMENT`, a certain finding. Such an element sits on the slide waiting for a phrase nobody
wrote, so the reveal never fires. It is the second line of the sample above, the one that names `2.1codex`.

Add the cue to `cues.json`, or drop the attribute from the page. An id that a script builds from a
variable, such as `data-cue="${IDS[i]}"`, is not a literal that any reader of the file can resolve, so
`align` leaves it to `decktalk preflight`, which reads the page's own catalog.

## Unresolved cues

If the `align` stage cannot match a phrase, it notes `phrase not found` and exits 1. These lines come
from a copy of the starter whose cue `2.1formula` has the typo "the formular".

```text theme={null}
sec  speech   need  cues
 02    14.6      -  2.1average@1.44,2.1code@9.43,2.1waited@11.31
                    ! 2.1formula: phrase not found: 'the formular'
3 sections with cues, 1 unresolved  (estimated words: times are placeholders)
```

`decktalk build` stops after the `align` stage, because a slide whose only cues are unresolved never
mounts. Fix the phrase, or pass `build --allow-unresolved-cues` to build without the cue. The page
then warns that the cue is not in `?cues=`, and its element appears at its `data-delay` time. The
`align` command itself has no such flag, because reporting the phrase is its whole job.

A cue that resolves past the end of the section's audio stays, with a note. A build without voice estimates
word times, so its cue times are estimates too. The table then ends with
`(estimated words: times are placeholders)`.

## Phrase choice

These guidelines help a reveal start on the right word.
[Writing for the ear](/guides/writing-for-the-ear) explains each one.

* Start the phrase with the word that names the thing that appears, because the cue time is the start of the first word. A noun such as `box of knobs` works, and so does a verb with its object, such as `steps downhill`. A connective such as "and then" does not.
* If a word repeats in the section, use two or three words, or set `occurrence`. Set `occurrence` to 1 to keep the first match and silence the warning.
* Set a small positive offset key, 0.1 to 0.2 s, so the viewer hears the word start before the element moves.
* Use a negative offset key only when the audience must read along.
* Put a `[beat]` or a `[pause N]` before a reveal that should feel like an event.
* Cue a summary on `$end` to show it as the voice stops.
* Write numbers as words in the script, and show digits on the slide.

## Sound on cues

A sound effect in `[[mix.sfx]]` names a `section` and a `cue`. The `assemble` stage places the sound
effect at that cue's time. Music marker uses the same `on`, `occurrence`, `case_sensitive`,
and `offset` keys to raise or mute the music at a phrase. [Sound](/concepts/sound) explains both.

## Terms

| Term                                | Meaning                                                                                                                    |
| ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| [cue](/reference/glossary)          | A cue is one entry in `cues.json`. It pairs a cue id with a cue phrase.                                                    |
| [cue id](/reference/glossary)       | A cue id ties a cue to the page. It is a slide id plus a short word, such as `1.1bowl`.                                    |
| [cue phrase](/reference/glossary)   | The cue phrase is the `on` value: a spoken phrase, `$start`, or `$end`.                                                    |
| [resolve](/reference/glossary)      | The `align` stage resolves a cue phrase to a cue time.                                                                     |
| [cue time](/reference/glossary)     | The cue time is the start of the cued word plus the offset key, rounded to hundredths, in seconds after the section start. |
| [offset key](/reference/glossary)   | The offset key is the cue's `offset` in `cues.json`. A positive value moves the cue time later.                            |
| [spoken words](/reference/glossary) | The spoken words are the words in the section's words file. Matching and `?words=` use them.                               |

## Next

* **Write phrases that time well:** [Writing for the ear](/guides/writing-for-the-ear)
* **Look up every key:** [cues.json](/reference/cues-json)
* **Learn which slide owns a cue:** [The page contract](/concepts/page-contract#cue-ownership)
