decktalk.toml sits in the project root, and every path in it is relative to that directory. It has
two halves, and knowing which half a key is in tells you everything else about it.
The document is the content this project is: what it is called, which section plays which page or
clip, which voice reads it, what the sound is made of and how the cuts behave. Every value in it
changes from presentation to presentation, and this page is its reference. Each number in it still
has a range the loader holds it to, which the tables below give and which decktalk schema setting
publishes under content with its default and the reason for each edge.
The settings are what a project changes. Every settings key publishes its default, its safe range, its unit,
its scope and what a value at the edge risks, all from one declaration in the code, so
decktalk schema setting, the JSON Schema, decktalk config explain and
Configuration are four renderings of one row. This page names the tables
and sends you there rather than copying the numbers.
The first line
decktalk.toml
decktalk init writes the line, and it costs nothing to keep.
Which value wins
A project key is set by its default, then bydecktalk.toml, then by the environment variable it
publishes, such as DECKTALK_VIDEO_PRESET, then by --set table.key=value for one run. A machine
key takes the per-machine file in place of decktalk.toml, and a project file that sets one is
refused. Which value wins is the rule in full, and
Keys only a machine may set lists the
machine keys.
--set writes nothing. --set overrides a key for exactly the run it is given to, and the
file on disk is untouched, which is what makes it the right way to try a value. It takes the same
spelling an environment variable takes, so a value cannot get in through one route that the other
would refuse. No flag on any command duplicates a settings key, so reduced motion is
--set motion.reduce=true and never a flag of its own.
decktalk config explain KEY prints the value that wins and the layer that set it, along with what
the key changes, what may be written into it and which findings it moves. Its --json lists every
layer that sets the key.
decktalk config set KEY VALUE is how you write one into a file. It reads the value under the same
range the loader enforces, edits the document in place rather than rewriting it, so the comments you
wrote around the key survive, and loads the would-be file whole before a byte lands, so a file no run
could use never reaches the disk. --scope project writes decktalk.toml and --scope machine
writes the per-machine file. --dry-run reports the change and writes nothing.
decktalk config unset KEY takes a key out again so the layer below it wins.
A value outside a key’s safe range is refused at load, by the dotted key that wrote it, with the
range it missed, the layer it came from and the sentence saying what goes wrong at the edge.
Load rules
Every project command loads this file first. The load fails when any of these is true, and the refusal names what it read and would not accept.- A top-level table is neither a document table nor a settings table.
- The file has no
[[section]]table. - A required key is missing, such as a
[[section]]with nonumber. - A value has the wrong type.
seconds = 120.5under[score.music]fails, because that key is an integer and a mapping carries the type its author wrote. - A number is
nanorinf, which TOML spells and no range can hold. The same refusal meets one in aDECKTALK_variable or a--set, and it names the file, the variable or the flag that wrote it. - Two
[[section]]tables share anumber. - A section names more than one of
page,clipandrender_command, or none of them. - A
render_commandis not a list of words, or holds an empty word. - A number in a document table is outside its range, such as
slate_seconds = -3or[mix] music_db = 40. The refusal names the line that wrote it. seamlessis set on the first section, which has no previous section.- A
[transition] dipspair or a[[mix.effect]]row names a section that does not exist. - A settings value is outside its safe range.
- A
pagesits at the project root. The origin serves a page’s whole directory, and the root holds the script, the cue file and the build directory. - A path the project names resolves outside the project, which includes a symbolic link inside the project that points out of it. The refusal names the path and never the place it resolved to.
- A settings key only a machine may set is written here, such as
record.concurrencyortools.ffmpeg. The refusal names the key and theconfig setline that puts it in the machine’s file.
The smallest file that loads
decktalk.toml
decktalk init writes a three-section file with comments and every section a page section.
decktalk init --example NAME writes a finished project instead.
The document
The tables below are the content this project is. Every one of them is read byDocument, none of
them has a settings key in it except where this page says a table is shared, and none of them
publishes an environment variable or answers to --set.
[project]
[[section]]
Write one [[section]] per section of the film. Sections are sorted by number, so the order in the
file does not matter. A section is a page section, which records one scene of an HTML page, a
render section, which is narrated like a page section and drawn by a program you name, or a
clip section, which plays a video file you already have. A page key makes a page section, a
render_command key a render section and a clip key a clip section, and naming more than one of
them or none fails the load.
Every section
A page section
lead_seconds and tail_seconds are placed when the takes are joined and are never sent to the
voice, so changing either voices nothing. The section starts where its silence starts, so
lead_seconds moves every word and every phrase cue of the section later while a $start cue stays
at zero. A change reaches the clock when narrate joins the takes again: until then words, cue and
record read the lead the take index holds, which is the lead the narration plays.
script.md has the whole rule.
record sets t0 itself so that the page starts its clock on the recorder’s signal, and it fills
cues, words and turns from what cue and narrate resolved unless the section names them. A frozen
frame, which storyboard and check draw, sets cues, t0, freeze, after and before itself,
so those five are dropped from a section’s params rather than passed through.
decktalk.toml
page sits in and for the files the document names, which are each clip,
every [mix] file, every [[mix.effect]] file and every [score] out. Every
other file in the project is refused, so the script, the cue file, build/ and .env stay out of a
page’s reach. A declared file is reached by the spelling written here and by no other, so
media/Theme.mp3 does not answer a request for media/theme.mp3 on any platform. A declared
directory is a place, so every name under it is served however the page spells it.
A render section
A render section names the program that draws its picture in place of a page. It is narrated exactly as a page section is: its take sets its length,cue resolves its cues from its words, and
the narration runs through it without a pause. It names no page and no scene, and it takes the
page section keys hold_seconds, lead_seconds, tail_seconds and with_ambience with the same
meaning. A scene or a params table written on it is ignored, and the load warns that the key
applies only to a page section.
decktalk.toml
record.programs, which
refuses them by default, and never under record.page_policy = "untrusted". It runs with your user’s
rights and can read every file you can, .env included: the scrubbed environment it is given keeps a
credential in the shell from it, and nothing keeps a file on disk from it. It runs in an empty
scratch folder, and a word that names a project file by a relative path is handed over as that
file’s absolute path. check raises PROGRAM_NAMED for every program a project names.
Programs a project names says how to allow them. A section whose program writes no landings is drawn and played, and verify measures none
of its cues.
A render section shows the whole contract.
A clip section
A clip whose file is not there plays a titled slate for
slate_seconds, and decktalk status
raises FILE_MISSING unless the section sets optional. The speech inside a clip is captioned from
the words file beside it, <clip>.words.json. The voice never reads a clip section, so it
needs no heading in the script and it is never voiced. A clip between two page sections pauses the
narration, which resumes with the next page section. DeckTalk scales and pads the clip to the frame
size, matches its sound to the narration’s loudness, and the chapters include it.
Play a clip shows the whole task.
Every media file a project names, which is a clip, the music, the ambience and each effect, is read
as one file by one of a closed set of formats: MP4, MOV and M4A, WebM and MKV, AVI, MP3, WAV, Ogg,
FLAC, AAC, PNG, JPEG and WebP. A file in any other format is refused, and so is a playlist, an HLS
or DASH manifest, an ffmpeg concat list or an image sequence, because each of those opens files or
hosts the project never named.
[voices.NAME] and [narration] voice
Each voice a project reads in is one [voices.NAME] table, and [narration] voice names the default
voice, which reads every section no [voice NAME] line in the script names. A voice name is a
lower-case letter followed by lower-case letters and digits, such as narrator, guest or host2,
so it spells one environment variable. Everything a provider is particular about is in that
provider’s own table, [elevenlabs] for ElevenLabs and [dtsp] for the local voice, and a voice
may set any of that table’s voice settings for itself alone.
decktalk.toml
Any other key of a voice table is a voice setting of its provider’s table, such as
model,
stability or output_format, and its value wins over that table’s for this voice, whatever layer
set either. A key of another provider’s table is refused when the project loads, and so is the rate
a provider charges, which every voice of that provider is billed at, and a key only the machine may
set, such as base_url.
A project that writes no [voices.NAME] table is read by the default voice at every default. Once
one table is written, every voice the project reads in needs its own, the default included, and
[narration] voice must name one of them. A variable or a --set for a voice the project does not
declare is refused, because a voice renamed in the file would otherwise leave its variable buying in
the wrong voice. A voice’s name is
never part of a take’s digest, so renaming a voice, with its [voice NAME] lines, voices nothing.
Write a dialogue shows a section read by two voices.
The ElevenLabs API key is the one secret, so it lives in .env as ELEVENLABS_API_KEY, or in the
environment. The local voice needs none.
[elevenlabs] holds model, stability, similarity_boost, style, speaker_boost,
dollars_per_1000_characters, output_format and base_url. ElevenLabs bills per character, so its
rate is stated per thousand characters. model is the model every ElevenLabs voice reads in unless
its own table sets one. The score stage buys from the same API with the same key and
base_url, and its own request settings and its rates per minute of audio are in [score].
Configuration has each one with its range. The voice id, the
model, speed, the four voice fields and output_format are part of a take’s input digest, so
changing one makes the next voiced build voice again every turn that voice reads. The price and the base URL are
not. base_url is machine-scoped, so it is set in this machine’s file and a decktalk.toml that
sets it is refused.
[dtsp] holds model, and the machine-scoped base_url and handshake_seconds, for the local voice, which speaks the DeckTalk speech protocol
to decktalk-voice, a separate local server that is not part of this repository and is not yet public. base_url is
machine-scoped like ElevenLabs’s, so only this machine names where the script is sent, and
handshake_seconds is how long the server has to say which aligner it serves. The local voice bills nothing, so buying its takes asks for no
--spend, and a build still asks about a score it would buy. Its takes are named by dtsp, the voice id, the model and speed, so they never share a
name with an ElevenLabs take of the same words. Configuration has
all three keys.
[score]
[score] holds the prompts that decktalk score generates from. It is optional, and no
packaged project writes one. Every sound it buys is written to the score directory, [score] dir,
which is score/ by default and which you commit, unless the item names another path. A generated
file that [mix] then names is what the film actually plays.
Each item’s table is shared: its prompt and its file are the document, and beside them sit the
settings that size its request, spelled the same way in every table. Configuration
has every setting with its range, and [score] timeout_seconds is how long one request may take.
decktalk.toml
[score.ambience]
[score.effects] and [score.effects.<name>]
[score.effects] holds four settings for every effect: duration_seconds, which is 0.5,
prompt_influence, which is 0.5, model, and dollars_per_minute, which is what your plan
charges for a minute of effect audio. One [score.effects.<name>] table per effect
names it, and <name> is the name the file is written under. It takes prompt, which is required, and
out, which is score/<name>.mp3 by default, and it may set duration_seconds, prompt_influence or
model for itself. A [[mix.effect]] row is what places the finished sound in the film.
[score.music]
A piece longer than
chunk_max_seconds is asked for in parts and joined with a crossfade, because the
service will not write a long piece in one answer.
[mix]
[mix] is shared too. The keys below are the document, naming the sound files the film plays and the
level each one plays at. The settings half is how each layer is ramped, and
Configuration has its five keys. The loudness the final mix is
mastered to is [audio].
Without [mix] the film carries the narration and the audio of any clip, and nothing else.
A missing music or ambience file does not fail the build.
assemble warns and plays the film
without it.
[[mix.effect]]
One table per sound effect, each of which plays at a cue.
A missing file or a cue that never resolved does not fail the build.
assemble skips that effect and
says so.
markers.json
[mix] music_markers names a JSON file that shapes the music under the film. No packaged project
writes one, so the block below is the shape rather than a starter’s values.
media/markers.json
A marker resolves exactly as a cue does, against the words of its own section. A marker whose phrase
is not spoken is reported by
assemble and skipped.
[transition]
Audio never dips. A dip shows on screen, so leave a pair out of
dips for a cut that should not
show, which is also what a seamless section needs.
The settings
A settings table may be written here with its project keys. Its machine keys go in the per-machine file instead, and[tools] holds nothing else, so it is never written here. These are the tables,
and every key of each one is published with its default, its safe range, its unit, its scope and
its hazard by decktalk schema setting and documented in full on
Configuration.
decktalk config list prints every key with the layer that set it.
The captions of a section spoken in more than one voice carry the words alone by default. Turn on
caption_voices to name the voice of every one of those captions, as a WebVTT voice span in the
.vtt and as the name before the line in the .srt:
.srt writes the same caption as engineer: Says who? Every demo says that., and those turns
are wrapped short enough that the name and the line still fit on one caption line.
captioned = true also writes <name>-captioned.mp4, the film with its captions drawn into the
picture by your page, for a place that cannot load a caption file. The film itself is the same
frames either way. Accessible video
says how a page styles the captions and places them by voice.
Related
- Write the script: script.md
- Land a reveal on a phrase: cues.json
- Change a setting: Configuration
- See how the sound fits together: Sound