Skip to main content
script.md holds the words that the voice says, split into numbered sections. This page lists its heading rules, its stage directions, and how DeckTalk cleans the text before the voice receives it.
script.md

Headings

A numbered heading starts a section. Any other heading ends it. Note: the times in a budget are M:SS. The budget’s length is the target column of the narrate table. A ### heading inside a section does not end it. narrate removes the ### marks and the voice speaks the heading text. The script and decktalk.toml must agree on section numbers.
  • The script needs at least one numbered heading.
  • Every numbered heading needs a [[section]] table with the same number. If one is missing, narrate fails with script sections [N] have no [[section]] in decktalk.toml.
  • A page section needs a ## N. heading. Without one, record skips the section and assemble fails.
  • A clip section does not need a heading. narrate never voices a clip section, so its body can describe what plays.
  • Text before the first numbered heading is never spoken. The scaffold keeps its notes there, above a --- line.

Directions and placeholders

Text in square brackets is not spoken. A direction splits the text at its place. A beat adds a dash to the text before it, and the voice pauses at the dash. A timed pause adds a break tag instead. [pause N] is the only direction that sends a break tag. Writing for the ear explains when to use each. narrate refuses a section that still has a placeholder. Pass --allow-placeholders to voice it anyway. decktalk narrate --dry-run lists every placeholder.

How directions combine

Silence that DeckTalk adds

A page section in decktalk.toml can add its own silence. ffmpeg adds this silence to the audio. The speech provider does not receive it. lead_seconds goes into narration.mp3 before the section’s mp3, which stays as the voice returned it. The tail is not part of the narration cache. If you raise min_tail_seconds, the next narrate pads each cached mp3 and sends no request. narrate measures the tail before it pads. A silence that ends within 0.06 s or one mp3 frame of the end of the file counts as the tail, because the mp3 encoder adds up to 50 ms of padding there. A cached mp3 that narrate already padded is not padded again when its tail is within 0.06 s of min_tail_seconds. So a second run leaves each cached mp3 and its duration unchanged. Configuration lists every [narration] key.

Markdown stripping

narrate cleans the text of each section in this order.
  1. narrate replaces a link [text](url) with its text.
  2. narrate turns each direction into a pause mark.
  3. narrate removes the markers of inline code, bold, and emphasis.
  4. narrate removes heading marks, list bullets, and list numbers at the start of a line.
  5. narrate joins the lines of a paragraph with single spaces. A blank line separates paragraphs.
  6. narrate replaces each pause mark with a dash or a break tag at the end of the text before it.
The speech provider receives the text with its dashes and break tags. The spoken words have neither. Cue matching and captions use the spoken words, so write each cue phrase with plain words. decktalk narrate --dry-run prints the exact text for each section.

Length

The narrate table and a silent build estimate each section’s length with these formulas.
words is the number of spoken words. Timed pause seconds add up every [pause N] and every written break tag. The defaults are 140 for words_per_minute and 150 for silent_words_per_minute. direction_break_seconds, lead_break_seconds, and min_tail_seconds are each 0.7 by default. Note: an em dash with a space before it counts as a beat, even when you type it in the prose. A voiced section lasts as long as the audio that the speech provider returns, plus the added silence.

Numbers and symbols

Write numbers and symbols the way the voice should say them. Cue phrases in cues.json match spoken words, so the script and the cue must use the same words. The scaffold writes “decktalk dot app” in the script and shows decktalk.app on the slide. A cue on “decktalk dot app” resolves. A cue on “decktalk.app” matches nothing, because matching removes the dot and leaves one token.