Narration
AINarration reads your speaker notes aloud and turns the pages as it goes: each [click] part of a
slide’s notes is spoken at its step, then the deck moves on. Use it to rehearse a talk, to hear how
long it really runs, or to leave a deck that presents itself.
It needs nothing but notes. A slide’s <Notes> (or the comment at its end) is what the narrator
says, split at [click] markers:
## Three steps
<Clicks>
- Write- Present- Publish
</Clicks>
<Notes>Three steps today. [click] First, writing. [click] Then presenting. [click] And publishing.</Notes>Start it
Section titled “Start it”- In the presenter view, the narration bar sits along the bottom: play, previous and next, stop, speed, volume, the voice and the engine.
- On the deck itself, press R to start or pause, or open the deck with
?narrateto show the bar there.
The timeline marks a chapter at each layout: section slide (or at each column, in a deck with no
sections), and a small point for every slide. Hover it to see the slide and when it starts; click to
play from there. With the timeline focused, the arrow keys move a slide, Page Up and
Page Down (or Shift with an arrow) move a chapter, and Home and
End go to the ends.
Follow the narrator, on by default, lets the narration turn the deck’s pages; off, it reads on and leaves the deck where it is. Because the pages turn through the same commands as the keyboard, the projector window, the phones in the live room and the phone remote follow along.
Speed, volume, voice and engine are remembered in the browser.
The voices
Section titled “The voices”Four engines sit behind one bar. With engine: auto (the default) each segment uses the first that
can say it: a baked clip, else Kokoro once it has loaded, else the browser’s voice.
| Engine | What it is | Needs |
|---|---|---|
baked |
audio files made ahead of time by kadal-deck narrate, played from public/narration/ |
running kadal-deck narrate once |
kokoro |
the Kokoro-82M model running in the browser in a Web Worker (WebGPU, or WebAssembly where there is none) | kokoro-js installed in the deck; the model downloads on the first play and is cached |
browser |
the browser’s own speech (speechSynthesis): instant, no download, sounds different on every system |
nothing |
silent |
says nothing, but waits as long as saying it would take | nothing |
Kokoro is an optional dependency of a few hundred megabytes. Install it into a deck with:
npx kadal-deck narrate --setuppnpm exec kadal-deck narrate --setupyarn kadal-deck narrate --setupbunx kadal-deck narrate --setupWithout it, nothing of Kokoro is built into the deck and the narrator uses the browser’s voice.
Settings
Section titled “Settings”Set narration in the headmatter:
---title: My talknarration: voice: bm_george # a Kokoro voice rate: 1.1 # 0.5 to 3 gap: 600 # milliseconds between steps silent: estimate # a slide without notes: estimate, skip, or milliseconds---| Key | Default | Meaning |
|---|---|---|
enabled |
auto |
auto turns narration on when some slide has notes; false turns it off |
engine |
auto |
auto, baked, kokoro, browser or silent |
voice |
af_heart |
the Kokoro voice; also the voice baked clips are made and looked up with |
rate |
1 |
speed, from 0.5 to 3 |
gap |
600 |
milliseconds between one step and the next |
silent |
estimate |
how long a slide without notes stays: estimate (from its words), skip, or milliseconds |
model |
onnx-community/Kokoro-82M-v1.0-ONNX |
the Hugging Face model Kokoro loads |
dtype |
auto |
Kokoro’s precision: auto, fp32, fp16, q8, q4 or q4f16 |
narration: false in the headmatter turns narration off for the whole deck.
The Kokoro voices: American af_heart, af_alloy, af_aoede, af_bella, af_jessica,
af_kore, af_nicole, af_nova, af_river, af_sarah, af_sky, am_adam, am_echo,
am_eric, am_fenrir, am_liam, am_michael, am_onyx, am_puck, am_santa; British
bf_emma, bf_isabella, bf_alice, bf_lily, bm_george, bm_lewis, bm_daniel, bm_fable.
Per slide
Section titled “Per slide”---say: This is what the narrator says here, instead of the notes. [click] And at the first click.---say: replaces the slide’s notes for the narrator (its [click] markers split it the same way);
an empty say: "" keeps the slide silent. narration: false on a slide says nothing there, and
the slide stays as long as silent says.
Bake the narration
Section titled “Bake the narration”Kokoro in the browser has to download the model and synthesise each sentence while the talk plays. Baking does the work once, on your computer, so the deck plays files:
npx kadal-deck narrate --setup # once: installs kokoro-js into the decknpx kadal-deck narratepnpm exec kadal-deck narrate --setup # once: installs kokoro-js into the deckpnpm exec kadal-deck narrateyarn kadal-deck narrate --setup # once: installs kokoro-js into the deckyarn kadal-deck narratebunx kadal-deck narrate --setup # once: installs kokoro-js into the deckbunx kadal-deck narrateEvery segment of every slide’s notes is spoken by Kokoro-82M on the CPU and written to
public/narration/<hash>.mp3, with an index in public/narration.json. kadal-deck build and
kadal-deck publish ship them with the deck, and the baked engine plays them, in the voice they
were made with.
- With
ffmpeginstalled (withlibmp3lame), clips are MP3; with onlylibopus, Opus; with noffmpeg, WAV, about ten times larger. - A clip that exists is skipped, so after editing one slide, running it again speaks only what
changed.
--forcespeaks every clip again. --voice <id>picks the voice (default: the headmatter’snarration.voice, elseaf_heart), and--dtype fp32|fp16|q8|q4|q4f16the precision (defaultfp32).--jsonprints the result: segments, clips made and skipped, bytes, the format and the index.
Clips are keyed by voice and text. If the deck’s voice doesn’t match the baked one, the narrator
falls back to Kokoro or the browser.
Licences
Section titled “Licences”kokoro-js, transformers.js and the Kokoro-82M weights are Apache-2.0, and the ONNX runtime is
MIT. The
phonemizer they use compiles in eSpeak NG, which is GPL-3.0-or-later, so a build of a deck with
kokoro-js installed carries eSpeak NG’s terms. That is compatible with the engine’s AGPL; the
details are in the engine’s
THIRD_PARTY_NOTICES.md.
A deck that doesn’t install kokoro-js contains none of it.