Docs
Settings
Every setting in video.json, what you can set for one scene in script.md, and the environment variables.
What goes in video.json?
Every setting is optional. A small file looks like this:
{
"title": "How DNS finds a website",
"theme": "chalk",
"voice": "af_heart",
"music": "calm"
}
| Setting | Default | What it does |
|---|---|---|
title |
from the script | the title shown in the preview |
theme |
studio from init, paper otherwise |
a built-in look (studio, paper, clean, chalk, blueprint, midnight, editorial), or a relative path to a JSON look file. See the looks |
size |
16:9 |
the size of the video. A platform name (youtube, shorts, tiktok, reels, vertical, instagram, linkedin, square), a ratio (16:9, 9:16, 1:1, 4:5) or WIDTHxHEIGHT |
sizes |
none | settings for one size, like { "tiktok": { "captions": "bold", "pace": 1.2 } }. A block can set captions, cover, pace, watermark and targetSeconds. See settings for one size |
fps |
30 |
24, 25, 30, 50 or 60 frames per second |
language |
en |
the narration language: en, es, fr, it, pt-br, hi, ja or zh. de works with a paid voice or a recording |
languages |
none | settings for one language, like { "es": { "voice": "ef_dora" } }. See several languages |
voice |
the language’s voice | a Kokoro id, or an object for Kokoro, ElevenLabs, Cartesia or a recording. English uses af_heart |
speed |
0.9 for Kokoro, 1 otherwise |
how fast the voice speaks, from 0.6 to 1.6. ElevenLabs limits speed times pace to 0.7 through 1.2, and Cartesia to 0.6 through 1.5 |
pace |
1 |
how fast the whole video runs, from 0.7 to 1.6. It changes the voice, the pauses and the animations |
targetSeconds |
none | the length the video should have, like 40. A shorter video gets the missing time spread over its scenes’ holds. voice and check warn when the video is more than half a second longer. See video length |
music |
true |
true plays the music style that goes with the look. You can also name a style (warm, upbeat, calm, tech, playful), set a volume with { "volume": 0.35 }, which keeps the look’s style, use your own file with { "src": "assets/track.mp3" }, or turn music off with false. See your own music |
sfx |
true |
sound effects. true, "minimal" or false |
captions |
"auto" |
true, false, "auto" or "bold". With "auto", tall, 4:5 and square videos get captions and wide ones don’t. "bold" shows two to four big words at a time |
cover |
"auto" |
a full-size JPEG without captions. With "defaults": "0.5", auto takes the fullest frame of the first scene. With "defaults": "0.6", it looks in the first scene that shows a picture, like a recording or a clip. You can use 12.5, "intro", "intro@start", "intro@2.4" or "intro@end" |
transition |
"auto" |
how scenes change. "auto" uses the change that goes with the look. You can also pick fade, slide, push, wipe, zoom, brush or cut. push moves the new scene in and the old one out side by side, so the text of two scenes never shows in one place |
camera |
"auto" |
"auto" films each scene with a camera that follows the narration. "still" keeps each frame as you wrote it. See how the camera works |
motionBlur |
true |
motion blur on moving frames in the final render. Drafts never have it, and render --quality fast leaves it out for one run |
lead |
0.35 |
how many seconds pass before the voice starts in each scene |
hold |
0.7 |
how many seconds the scene stays after the voice has finished |
end |
1.4 |
extra seconds at the very end of the video |
sentenceGap |
0.3 |
the pause between two generated sentences, in seconds. Recordings keep their own pauses |
paragraphGap |
0.55 |
the pause between generated paragraphs, in seconds. Recordings keep their own pauses |
loudness |
-14 |
how loud the finished video is, in LUFS |
boil |
0 |
how many times per second the hand-drawn lines are redrawn. 0 keeps them still |
watermark |
"explainroo.com" |
the small text in the bottom right corner, or false for none. A TikTok package always leaves it out |
images |
none | settings for AI images, like { "model": "best", "style": "..." } |
clipBudget |
none | the most all AI clips of the project may cost together, in US dollars, like 0.40. See a budget for clips |
heard |
none | forms the speech check accepts for a word, like { "regex101": ["reg x 101"] }. See the speech check |
brand |
none | colors, fonts and logo for s.ui, as described below. Its accent also colors the spoken word in captions, and with "defaults": "0.6" the *starred* words of s.title and s.text |
fonts |
[] |
your font files, as described below |
defaults |
none | "0.6" gives the project the defaults of explainroo 0.6, which include those of 0.5, as described below. init writes it |
If a setting is unknown or out of range, explainroo stops and tells you what is wrong.
How do I change one setting from the command line?
explainroo set writes one setting into video.json:
npx --yes explainroo set videos/dns targetSeconds 40
npx --yes explainroo set videos/dns sizes.shorts.pace 1.2
A dotted key like sizes.shorts.pace sets a setting of one size. The value is JSON when it reads as JSON, like 40 or {"pace": 1.2}, and text otherwise. So set videos/dns brand.accent '#e8a24a' needs no quotes around the color. null takes the key out. set prints the value it wrote with its type, and says so when a setting changes type, like music from true to a block. The settings get the same check as every command first, so a mistake changes nothing, and the rest of the file keeps its order.
What does the defaults line do?
The "defaults" line keeps a video looking the same when you update explainroo. A new version may draw some things in a new way, like the size of arrow labels. It does so only in a project whose line names that version or a later one.
init writes "defaults": "0.6" into every new project, so a project made now keeps the look of 0.6 after later updates. A project with "0.5" keeps the look of explainroo 0.5. A project without the line keeps the look it had before 0.5, frame for frame, and also its cover.
To give an older project the new look, set the line to "0.6". Then look at still and sheet again, because labels may move. check finds the same problems with or without the line.
What does “defaults”: “0.6” do?
"0.6" has everything that "0.5" changes, which the next section lists. It also changes these things:
- A title or a box label that wraps breaks into lines of about the same length, so no word is left alone on the last line. Subtitles and text on a card of its own (
bg) do the same.balance: falsekeeps the old lines. - Text follows one size scale. Words on a card are 44 pixels, and the labels of arrows and icons are 40. Chart labels are 36 pixels and notes 34. The values on bars are 48. Before, an arrow’s label was 48, larger than the card it points at.
- A list is 48 pixels, and its items sit further apart.
- A bar for a small value stays at least 8 pixels tall, so it doesn’t vanish into the axis.
- A highlighted line of code gets a bar in the accent color at its left edge.
s.numberwrites numbers the way the video’s language does, like1 050in French.- On Shorts, TikTok and Reels, pictures and backgrounds may fill the whole frame, past the content area. Text still stays in the content area. See filling the frame.
"cover": "auto"takes the fullest frame of the first scene that shows a picture, like a recording or a clip. When no scene shows one, it takes the fullest frame of the first scene, as with"0.5".brand.accentalso colors the*starred*words ofs.titleands.text, andbrand.backgroundis the background of every scene. An accent that is hard to read there keeps its hue and gets lighter or darker.- Words that the kits and formats draw by themselves, like “STEP” in
s.short.step(), come in the video’s language.
What does “defaults”: “0.5” do?
"0.5" changes these things, and "0.6" has them too:
- Arrow labels are body size, 48 pixels, and sit 44 pixels from the arrow. Before, they were 34 pixels, 34 pixels from it.
- An arrow to or from an icon with a label starts and ends outside the label.
gapon an arrow also leaves that many pixels free at an end that is a point.- When the camera moves to an element, the shot also takes in the arrows and marks attached to it.
- A mark from
s.annotateon an element’sidleaves with that element, unless the mark has anoutof its own. Before, it stayed until the end of the scene. - A terminal without an
atof its own comes in with its first line. Before, it came in at the start of the scene and stayed empty until that line. - The default red of the
*keywords*on a sticker froms.shortis picked to read on that sticker’s color. - A
brand.accentthat is hard to read on the caption background keeps its hue in the captions, made lighter or darker. Before, the captions used the look’s highlight. "cover": "auto"takes the frame of the first scene with the most elements fully there. A frame with a picture in it comes first. Code that is still typing and a number that is still counting don’t count as there yet, so the cover never shows them half done.
Which forms can voice take?
A string such as "voice": "af_heart" uses Kokoro. These are the object forms:
| Provider | Example | What it needs |
|---|---|---|
| Kokoro | { "provider": "kokoro", "id": "af_heart" } |
a voice for your video’s language |
| ElevenLabs | { "provider": "elevenlabs", "id": "JBFqnCBsd6RMkjVDRZzb", "model": "eleven_multilingual_v2" } |
your API key. model is optional and defaults to eleven_multilingual_v2 |
| Recording | { "provider": "recording", "src": "assets/narration.m4a" } |
a file or folder inside assets/, with no .. in the path |
| Cartesia | { "provider": "cartesia", "id": "<voice id>" } |
your API key. model is optional and defaults to sonic-3.6 |
Leave voice out to use the language’s default. If you change language, change an existing Kokoro voice too, or remove it. See voices and languages for choosing voices and preparing recordings.
How do I set my own look?
Set "theme": "acme-look.json" to read a look file in your project. "theme": "../brand/acme-look.json" shares a file between projects. The path is relative to the project folder. The file must extend a built-in look. See custom looks for the file’s settings.
How do I set the brand and fonts?
brand gives product demos the app’s colors, fonts and logo:
{
"theme": "studio",
"brand": {
"accent": "#245cff",
"background": "#f8f6f1",
"ink": "#141413",
"font": "Figtree",
"headline": "Instrument Serif",
"logo": "assets/logo.svg",
"colors": { "text": "#141413", "line": "#e8e6dc" }
},
"fonts": [
{
"family": "Figtree",
"src": "assets/fonts/Figtree-600.woff2",
"weight": 600
}
]
}
accent is the main button color. background sits behind the screens, and ink is text on it. font is the app’s font and headline is the font for big headlines. logo is an image in assets/. colors can replace text, soft, muted, faint, line, field, surface and panel in the UI kit. Brand colors use six-digit hex values.
Inter and Instrument Serif are built in. For other fonts, add one entry per weight to fonts. Each entry needs family and src. Files must stay in assets/ and end in .woff2, .woff, .ttf or .otf. weight defaults to 400. A variable font can use "weight": "100 900". style is "normal" or "italic". Check each font’s license and keep its license file beside it.
What can I set for one scene?
In script.md, a scene can have its own settings. They go in braces after the scene name, like ## outro {hold=2 transition=cut}:
| Setting | What it does |
|---|---|
hold |
how long this scene stays after the voice, instead of the video’s hold |
lead |
how long this scene waits before the voice, instead of the video’s lead |
min |
the shortest this scene may be |
transition |
how this scene comes in |
chapter |
a title in quotes, from 1 to 60 characters, starting a chapter at this scene |
See writing the script for an example.
Where do keys go?
explainroo connect <service> sets up a key or a sign-in. It says what the service costs and what leaves your computer. Then it tests the key, and it saves the key only when the test works. In a terminal it asks for the key with a hidden prompt. A key never goes on the command line.
A key is a line like OPENROUTER_API_KEY=... in a .env file. connect saves it in your settings folder, so every project can use it. doctor prints where that file is. It is the first of these that applies:
$EXPLAINROO_CONFIG/.env, when that is set%APPDATA%\explainroo\.envon Windows$XDG_CONFIG_HOME/explainroo/.env, when that is set~/.config/explainroo/.env
In a clone of explainroo, connect uses the .env in that folder instead. --project <dir> saves the key in that project’s .env.
explainroo uses the first value it finds, in this order:
- the environment
.envin the project folder.envin a clone of explainroo, when it runs from one- the settings folder
New projects list .env in their .gitignore, and connect refuses a project .env that git already tracks. An older project may not list it, so check before you put a key there. Never put keys in your script or scenes.
Which environment variables are there?
| Variable | What it does |
|---|---|
OPENROUTER_API_KEY |
your OpenRouter key for AI images and clips |
ELEVENLABS_API_KEY |
your ElevenLabs key |
CARTESIA_API_KEY |
your Cartesia key |
BLOTATO_API_KEY |
your Blotato key for posting |
PRESENTER_API_KEY |
your key for a presenter service |
EXPLAINROO_PRESENTER_URL |
the address of the presenter service you picked |
EXPLAINROO_CONFIG |
the settings folder, instead of the usual place |
EXPLAINROO_ESPEAK |
where the espeak-ng binary is, if explainroo cannot find it. You can set it once in the settings file |
EXPLAINROO_PYTHON |
the Python that has misaki, for Japanese and Chinese |
EXPLAINROO_CHROME |
where Chrome or Chromium is, in case explainroo doesn’t find it on its own |
EXPLAINROO_FFMPEG, EXPLAINROO_FFPROBE |
where ffmpeg and ffprobe are |
EXPLAINROO_CACHE |
where the speech models are stored, ~/.cache/explainroo by default |
EXPLAINROO_OFFLINE |
set it to 1 and explainroo downloads no model files. services then asks OpenRouter for no prices |
EXPLAINROO_TTS_DTYPE |
the precision of the voice model. fp32 is the default. q8 is a smaller download, but it runs slower |
EXPLAINROO_DEBUG |
set it to 1 to see the full details of an error |
A sign-in to YouTube or Buffer saves its own lines in the same file. You don’t write those yourself.