decktalk narrate turns script.md into spoken takes with their words,
and decktalk cue turns each phrase in cues.json into a second on its section’s clock. Nothing on
a deck page says when a cue fires, so the only way to move a reveal is to change the words it waits for or
to nudge it off them.
This guide is about the two files that decide that timing: the sentences in script.md and the
phrase each row of cues.json names. Design a slide is about what the
picture does once the second is known.
Write the script before the slides
Write the whole script, then read it aloud once. The words that carry the argument are the words the reveals start on, so you need them before you can decide what appears. A slide drawn first usually needs a sentence nobody would say. Give each idea one reveal and each reveal one sentence. A lesson reads best at about one reveal every ten to fifteen seconds. Three reveals in one breath read as an animation rather than an explanation, and a minute with no reveal leaves the viewer nothing new to look at.Write numbers and symbols the way you want them said
The voice reads what it is given, and a cue phrase is matched against the words it said. So “41” is a guess about how the voice pronounces two digits, and no phrase you write can reliably match that guess. Write the words instead.
The starter’s script says “Make your own at decktalk dot AI” while its end card shows
decktalk.ai,
and the lesson example says “four hundred billion” while its slide shows 400 billion dials. The
number on screen and the number in the ear are the same figure written twice, once for each sense.
The SCRIPT_SPOKEN_SYMBOL finding
decktalk check scans the spoken text of every section and raises
SCRIPT_SPOKEN_SYMBOL for a section whose words hold a digit
or one of the characters $ € £ % & @ # * / + = < > ~ ^ |. The finding names the section and lists
the first five offending words.
--no-pages judges the written files with no browser at all, which is the fastest way to read this
one finding. The finding is a warning, because “41” may be exactly what you want the voice to try, so
it does not fail a run under the default --fail-on error. Two things clear it:
- Rewrite the word. Replace the digit or the symbol with the words the voice should say. This is the fix in almost every case, because it is also the only way a cue phrase can match.
- Accept it deliberately. Pass
--allow SCRIPT_SPOKEN_SYMBOLon the commands that would otherwise report it, and--fail-on warningwhen you want every warning to fail a run instead.
The beat marker and the timed pause
A short silence before a reveal makes the reveal an event. Bracketed directions are the only way a script asks for one, and they are never spoken.script.md
<break> tag, so
check reports a section with a [pause N] on any other model as SCRIPT_PAUSE_DROPPED and narrate refuses to buy
it, while a [beat] reads on every model. Back-to-back
directions do not add up and the longest one wins, and a direction before the first paragraph of a
section has nothing to pause after, so it is dropped.
What a script may not hold
Inside a paragraph,[beat] and [pause N] are the only bracketed spans allowed, along with an
ordinary Markdown link. Any other bracketed note inside a paragraph becomes a pause nobody asked for,
so decktalk narrate refuses it and decktalk check reports it as
SCRIPT_UNFINISHED with the line it sits on. A brace and an
HTML comment are refused for the opposite reason: the voice reads both of them out.
A bracketed word in capitals, such as [CUSTOMER_COUNT], is an unfilled blank. Write one wherever
you have a claim nobody has confirmed, because a voiced run would otherwise buy the claim as it
stands. SCRIPT_UNFINISHED is an error, so it fails a run until the blank is filled.
script.md lists every rule about the file.
Choose a phrase the voice really says
A cue row names a cue id and a phrase. The phrase is a word or a short phrase from that section ofscript.md.
cues.json
- It has to be spoken. The match is against the words of that section alone, and a heading, a bracketed direction and a word in another section are all invisible to it.
- It starts on the word that names the thing. A cue time is the start of the phrase’s first
word, so a phrase beginning “and then” lands the picture on nothing. Among the starter’s phrases
are
This is DeckTalk,what you will say,the slides,the formulaandin code. - It is two or three words long. A single common word such as “the”, “one” or “it” matches early and lands on the wrong sentence.
- It is copied, not retyped. The match drops case and every character but a letter, a digit and
an apostrophe, so
Four filesmatches “four files” andprojectdoes not match “project’s”.
"case_sensitive": true on a row when the case really is the difference between two matches.
A phrase that is never spoken is CUE_UNRESOLVED, which is
an error, because there is no second to put the picture at. Fix it by quoting the script again, not by
moving the limit.
occurrence, when the phrase is not unique
A repeated phrase matches every run of it, and the first match is usually too early.occurrence
picks which one, counting from 1.
cues.json
offset_seconds, which moves a cue off the start of its word
offset_seconds adds seconds to the resolved second. A positive value moves the cue later and a negative
value moves it earlier.
cues.json
A positive offset_seconds moves the cue time after the start of its word.
- A positive offset of 0.1 to 0.2 seconds. The reveal lands just after the word starts, while the word is still being said, which reads as the narrator pointing at something.
- A small negative offset. Use it only where the viewer has to read along, such as a line of code the voice reads out. The text must be on screen before its first word is spoken.
build/cue-times.json records the offset
separately so you can see what the phrase alone would have given.
end
Two anchors stand in for a phrase.$start is the section start, which is zero on its clock, and
$end is the end of the section’s last spoken word. Give the last reveal of a section $end and it
lands as the voice stops.
cues.json
$end is the end of speech and not the end of the section’s audio, so silence after the last word
belongs to hold_seconds on that [[section]] instead. cues.json lists
every key a row may carry.
Prove it before you spend anything
Three commands read the clock without buying a take.decktalk narrate --no-spend keeps every take on disk and writes a placeholder and its words
for each section that has none, and it spends nothing. On a project that has never been built, words has no clock to read until this runs.
decktalk words prints every spoken word with its start and its end, which is the clock every phrase
is written against. Before a voiced run the words are estimated from the text, and after one they are
the times the voice really took.
decktalk check judges script.md, cues.json and the pages together, prices the narration a
voiced build would buy and writes build/storyboard.html. It reports a moment on the page that no
row names as CUE_UNLISTED, a row the page does not declare as
CUE_UNKNOWN, and two cues close enough together to read as one
as CUE_OVERLAP. decktalk check --fix applies every safe fix,
and the fix for CUE_UNLISTED adds the row with an empty phrase for you to write the phrase into.
Next
- Make the picture that lands on the word: Design a slide
- Copy a pattern that already ships: Slide recipes
- Look up every key of the cue file: cues.json