Skip to main content
A film may be read by more than one voice. Each voice is a table of decktalk.toml, and a line holding only [voice NAME] in script.md hands what follows to that voice. A section that names no voice is read by the default voice whole, exactly as a one-voice film is. decktalk init --example interview my-interview writes a project whose second section is a dialogue between a narrator and a guest, both ElevenLabs voices with their ids left empty, so it builds on placeholders until you name them.

Declare the voices

Write one [voices.NAME] table per voice, and name the default voice in [narration] voice. A voice name is a lower-case letter followed by lower-case letters and digits.
decktalk.toml
Once one voice table is written, every voice needs its own, the default included. A voice may set any voice setting of its provider’s table for itself, and the two voices may sit on different providers. A voice id may also come from the environment as DECKTALK_VOICES_<NAME>_ID, such as DECKTALK_VOICES_GUEST_ID.

Hand the turns to them

In the section that is a dialogue, open every turn with a voice direction, the first included.
script.md
Each stretch one voice reads is a turn, and each turn is voiced as its own take, so changing the guest’s last sentence voices that turn alone. Where the voice changes, the turns are joined across [narration] turn_gap_seconds, 0.3 seconds by default, and a [pause N] written there wins, as the one second above does. The next section starts with the default voice again. Turns are counted from one within their section, so the guest’s last turn above is turn 4 of section 2. A take you do not like is bought again on its own with decktalk narrate --turn 2.4 --spend, which keeps every other turn’s take and sets the one it replaces aside, as Voice one turn again describes. A cue phrase in cues.json may sit in any turn of its section, because a section’s words run on one clock. The transcript names the voice before each turn of a section spoken in more than one voice, so a reader who cannot hear the film knows who says each line. The captions carry the words alone unless you turn on [output] caption_voices, which names the voice of every caption of a dialogue: <v guest> in the .vtt, and guest: before the line in the .srt.
The page knows who says each word whatever you choose: every item of DeckTalk.words carries its voice and its turn, and in the captioned copy that [output] captioned writes, every caption carries them as data-voice and data-turn. The interview example sets the guest’s captions on the right that way, under the guest’s bubbles, and no caption it draws names a voice. Accessible video has the whole of the captioned copy.

Check it before you build

decktalk check --no-pages reads the directions against the voices you declared, and says how much each voice reads and what it costs. decktalk status names the voices of every section, decktalk --json status carries the take state of every turn under each section’s turns, and once decktalk narrate has run, decktalk words heads each turn’s words with its voice.

Build it

decktalk build --no-spend plays a placeholder for every turn with no voiced take, so the pacing and the cues are real before anything is bought. A voice with no id cannot be bought: --no-spend plays its placeholders and the TAKE_MISSING finding of each of its turns names the [voices.NAME] id to set, and --spend stops at narrate with an INPUT error naming the voice, before anything is bought. With both ids set and the key in .env, decktalk build --spend buys every turn’s take. A voiced take that holds no sound plays its turn as silence and is reported as TAKE_SILENT, a warning that names the turn, so decktalk narrate --turn N.T --spend buys that turn again.