Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Voice — narrate your story

Add spoken narration to a Manic video with three ideas:

  1. Pick a voice once — voice("…")
  2. Speak full sentences — speak("…")
  3. Keep short on-screen text separate — say(…)

You do not time the audio by hand. Each speak line lasts as long as it takes to say — motion on that beat waits with it.

Voice is optional. Skip it when the video should stay silent; use say alone for captions.


say vs speak

sayspeak
What it doesChanges on-screen textSpeaks a voice-over line
Heard?NoYes
Needs voice(...)?NoYes — once per file
Best forShort punch lines, labelsFull sentences the viewer hears
Examplesay(cap, "GOAL");speak("The ball curls into the net.");

Rule of thumb: write the short caption with say, the full line with speak. They can run together on the same beat.

par {
  say(cap, "same grid → 3D");                          // what you read
  speak("Now lift the same grid into height.");        // what you hear
}

Or update a caption and speak in one call:

speak(cap, "Pull the camera back.");

How to add narration (step by step)

1. Put a caption box on the cast (optional but common)

text(cap, (cx, h*0.82), "");
size(cap, 28); color(cap, dim); wrap(cap, w*0.8);

2. Choose a voice once (near the top)

voice("cartesia");

You only need this if you use speak. One voice(...) per file is enough.

3. Narrate on the timeline

speak("Every world starts flat.");

Together:

title("A spoken beat");
canvas("16:9");
voice("cartesia");

text(cap, (cx, h*0.82), "");
size(cap, 28); color(cap, dim); wrap(cap, w*0.8);

par {
  say(cap, "cell by cell…");
  speak("Watch a map generate itself until the islands lock.");
}
wait(0.3);    // a short breath before the next idea

speak and voice always go together. Captions alone need only say — no voice line.


Choosing a speaker

voice takes up to four pieces:

voice(service, speaker?, pace?, language?)
voice("gtts");                         // simple built-in
voice("cartesia");                     // Skylar
voice("cartesia", "katie");
voice("cartesia", "jameson");
voice("elevenlabs");                   // Roger
voice("elevenlabs", "alice");
voice("elevenlabs", "jessica");
serviceeasy speaker names
Cartesiakatie, skylar, jameson, gemma, archie
ElevenLabsroger, alice, sarah, jessica, adam, george, liam, …

Your own speaker (provider voice id)

Paste the voice id from Cartesia or ElevenLabs when you have a favourite speaker that is not in the easy list:

// Cartesia — voice UUID from your Cartesia voice library
voice("cartesia", "a0e99841-438c-4a64-b679-ae501e7d6091");

// ElevenLabs — voice id from your ElevenLabs voice library
voice("elevenlabs", "21m00Tcm4TlvDq8ikWAM");

Easy names and raw ids both go in the same speaker slot.

Pace

voice("elevenlabs", "alice", "slow");     // normal | slow | fast
voice("cartesia", "katie", "fast");

Language

Defaults to English (en). Pass a language code when the narration is not English:

// language last (with pace)
voice("cartesia", "katie", "normal", "hi");
voice("elevenlabs", "alice", "normal", "es");

// or skip pace and put the language third
voice("cartesia", "katie", "hi");
voice("elevenlabs", "roger", "fr");

Common codes: en, hi, es, fr, de, pt, ja, zh, ko, it, ar, …

For the built-in gtts service, the second argument is the language:

voice("gtts", "en");
voice("gtts", "hi");

Narrate with motion (same beat)

Put caption, voice, and animation in one par so they land together:

par {
  say(cap, "fly the ridges");
  speak("Fly in low over the ridges.");
  orbit3(255, 15, 12, 3.8, smooth);
}

If the spoken line is longer than the motion, Manic holds the picture until the line finishes. A trailing wait(0.3) is only a pause between ideas — not a substitute for narration length.


Pacing — budget your Short by word count

You don’t set timings by hand, but you can predict how long a narrated video will run: a spoken line lasts about 2.5 words per second (≈150 words per minute — a natural narration pace).

Target lengthBudget for speak
15s Short~35–40 words
30s Short~75 words
60s Short~150 words

So a Short that feels right on paper can quietly balloon: 150 words of narration is a full minute, even if the script looks short. If a video runs long, trim the words first — the motion follows the voice, not the other way round.

Preview pacing without an API key. Manic estimates each speak line’s length from its word count even when text-to-speech is offline or no key is set (the line is silent, but the timeline still reserves the time). That means --still <t> lands on the same beat it will at full voice — so you can check timing and framing before spending a single TTS call:

manic monty-hall.manic --still 15.6   # the frame the viewer sees at 15.6s

A few practicalities:

  • Each line is clamped to 0.8s–30s — one very long speak won’t stall forever, but split big paragraphs into separate lines anyway; they read better.
  • wait(...) adds on top of the spoken time. Since speak already paces the beat, keep waits small (wait(0.3)wait(0.6)) — they’re breaths between ideas, not the pacing itself.
  • Real TTS duration replaces the estimate when a voice key is present; it lands very close, so your --still preview stays accurate.
  • Tip: to count your narration, add up the words inside every speak("…") and divide by 2.5 for a rough total in seconds.

Math in spoken lines

Write symbols as you would on screen; Manic reads them naturally:

speak("The slope is √(tan θ).");


Quick checklist

  • One voice("…") near the top when you want narration
  • Full sentences in speak("…")
  • Short on-screen lines in say(cap, "…")
  • Use par { … } when caption, voice, and motion should start together
  • Budget length by word count (~2.5 words/sec) — ~75 words ≈ 30s
  • Preview any beat with --still <t> (timing holds even with no TTS key)
  • No voice? Omit voice / speaksay alone is fine