Voice — narrate your story
Add spoken narration to a Manic video with three ideas:
- Pick a voice once —
voice("…") - Speak full sentences —
speak("…") - 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
say | speak | |
|---|---|---|
| What it does | Changes on-screen text | Speaks a voice-over line |
| Heard? | No | Yes |
Needs voice(...)? | No | Yes — once per file |
| Best for | Short punch lines, labels | Full sentences the viewer hears |
| Example | say(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?)
Easy names (recommended)
voice("gtts"); // simple built-in
voice("cartesia"); // Skylar
voice("cartesia", "katie");
voice("cartesia", "jameson");
voice("elevenlabs"); // Roger
voice("elevenlabs", "alice");
voice("elevenlabs", "jessica");
| service | easy speaker names |
|---|---|
| Cartesia | katie, skylar, jameson, gemma, archie |
| ElevenLabs | roger, 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 length | Budget 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
speakwon’t stall forever, but split big paragraphs into separate lines anyway; they read better. wait(...)adds on top of the spoken time. Sincespeakalready 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
--stillpreview 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 θ).");
Examples in the gallery
- Roberto Carlos’ Banana Kick — story VO + captions
- Your Map Is a 3D World — map → 3D with narration
- The Shortest Path — vector maze + A* with problem→solve narration (ElevenLabs)
- Made of Emoji — a vector spectacle with promo VO (ElevenLabs)
- The Monty Hall Problem — probability puzzle told in emoji, problem→solution→CTA narration (ElevenLabs)
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/speak—sayalone is fine