Skip to content

Narration

AI

Narration 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>
  • 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 ?narrate to 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.

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:

Terminal window
npx kadal-deck narrate --setup
Terminal window
pnpm exec kadal-deck narrate --setup
Terminal window
yarn kadal-deck narrate --setup
Terminal window
bunx kadal-deck narrate --setup

Without it, nothing of Kokoro is built into the deck and the narrator uses the browser’s voice.

Set narration in the headmatter:

---
title: My talk
narration:
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.

---
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.

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:

Terminal window
npx kadal-deck narrate --setup # once: installs kokoro-js into the deck
npx kadal-deck narrate
Terminal window
pnpm exec kadal-deck narrate --setup # once: installs kokoro-js into the deck
pnpm exec kadal-deck narrate
Terminal window
yarn kadal-deck narrate --setup # once: installs kokoro-js into the deck
yarn kadal-deck narrate
Terminal window
bunx kadal-deck narrate --setup # once: installs kokoro-js into the deck
bunx kadal-deck narrate

Every 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 ffmpeg installed (with libmp3lame), clips are MP3; with only libopus, Opus; with no ffmpeg, WAV, about ten times larger.
  • A clip that exists is skipped, so after editing one slide, running it again speaks only what changed. --force speaks every clip again.
  • --voice <id> picks the voice (default: the headmatter’s narration.voice, else af_heart), and --dtype fp32|fp16|q8|q4|q4f16 the precision (default fp32).
  • --json prints 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.

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.

Send feedback