Docs
Drawing scenes
How scenes.js works, how pictures appear on the right word, and where to put things on the screen.
What does scenes.js look like?
scenes.js has one function for each scene in script.md. The function gets s, which stands for the scene, and draws one frame of it:
export default {
hook(s) {
s.title("How DNS finds a website", { at: 0 });
s.icon("laptop", { x: 620, y: 660, size: 170, at: "#but" });
s.icon("server", { x: 1300, y: 660, size: 170, at: "name" });
s.arrow([700, 660], [1220, 660], {
dashed: true,
label: "by name?",
at: "name",
});
},
};
explainroo calls this function for every frame, with a fresh s each time. The whole scene depends only on the time. So a scene never keeps variables between calls and never uses timers.
How do I say when something appears?
Everything you draw is called an element. Every element takes at, the moment it appears, and can take out, the moment it leaves. Both accept three kinds of values:
s.box("Resolver", { at: 2.4 }); // seconds after the scene starts
s.box("Resolver", { at: "resolver" }); // when the voice says "resolver"
s.box("Resolver", { at: "#ask" }); // at the [#ask] marker in script.md
A word cue ignores capital letters and punctuation. A phrase like 'right server' works too. If the word is not in the narration, explainroo stops with an error and lists the closest words.
You can also work with the times yourself. s.cue('word') gives you the time of a word. s.mark('name') gives you the time of a marker. s.t is the current time in the scene. So s.cue('Finally') - 0.4 starts something a little before a word.
How do elements come in?
The look decides how things come in. In paper, chalk and blueprint, shapes are drawn line by line and text is written on. In clean and midnight, shapes pop in and text rises. You can pick another entrance for one element with enter, for example enter: 'pop' or enter: 'type'. exit picks how it leaves.
Most elements also make a sound when they come in. sfx: false keeps one element quiet.
Where do things go on the screen?
A wide video is 1920 by 1080 pixels. A tall video is 1080 by 1920. Positions count from the top left corner. x and y are the center of an element unless the reference says otherwise.
A few helpers save you the math:
s.safeis the area your content should stay in. It leaves out a margin around the edge, the captions when they are on, and on Shorts, TikTok and Reels the spots the app covers. Keep text inside it.s.cxands.cyare the center of the frame. On Shorts, TikTok and Reels they are the center ofs.safe.s.gridands.colcenter ons.safetoo.s.row(3, { width: 1100 })gives you three x positions spread over 1100 pixels.s.grid(3, 2)gives you six cells with their centers and sizes.
Most elements also tell you where they ended up. That lets you place the next thing below them:
const card = s.box("Cache", { x: 500, y: 540, at: 1 });
s.note("checked first", { x: card.x, y: card.bottom + 40, at: 1.4 });
How do I connect two elements with an arrow?
Give both elements an id and point the arrow at those ids. The arrow then stops at their edges:
s.box("Browser", { id: "a", x: 400, y: 540, at: 0 });
s.box("Resolver", { id: "b", x: 1100, y: 540, at: "resolver" });
s.arrow("a", "b", { label: "asks", at: "asks" });
Draw the elements before the arrow that points at them.
How do I keep a diagram from one scene to the next?
Each scene starts empty. To keep parts of the previous scene, draw them again with at: -1. They are then there from the first frame and make no sound. A small function in scenes.js that draws the shared parts keeps this tidy. The DNS example builds one diagram over several scenes this way.
What mistakes should I avoid?
- Drawing an element only sometimes, like
if (s.t > 3) s.box(...). Always call it and useatandout. The sound effects and the checks depend on that. - Pointing an arrow at an
idthat is drawn later in the function. - Calling
s.camera()after other elements. It has to come first. - An
atafter the end of the scene. The element never shows up. Make the scene longer with{hold=2}or{min=6}in the script. - Randomness from anything other than
s.rand(),s.noise()orMath.random()inside the scene function.
Every element and option is listed in the scene reference.