Docs
Scene reference
Every element and option you can use in scenes.js, with examples.
Time
| Member | What it gives you |
|---|---|
s.t |
seconds since the scene started |
s.T |
seconds since the video started |
s.dur |
how long the scene is, in seconds |
s.voice |
when the voice starts and ends in the scene, as { start, end } |
s.words |
every spoken word as { text, start, end }, in seconds since the scene started |
s.cue(word, n) |
the moment the voice starts saying a word or phrase. n picks the n-th time it is said, and 1 is the default |
s.cueEnd(word, n) |
the moment the voice finishes that word or phrase |
s.mark(name) |
the time of a [#name] marker in the script |
s.time(v) |
turns a number, a word or "#marker" into seconds |
s.pace |
the pace of the video. 1 is normal |
s.p(at, dur, ease) |
a number that goes from 0 to 1 while your own animation runs. dur is 0.6 and ease is inOut by default |
s.since(at) |
how many seconds have passed since a time. It is negative before that time |
s.between(a, b) |
true while the time is between a and b |
s.video |
facts about the whole video, as { duration, frames, fps, width, height, scenes } |
The easing names are linear, in, out, inOut, outBack, outElastic, outQuart and inOutSine. For your own math you can use s.ease.out(p), s.lerp(a, b, p) and s.clamp(v, lo, hi).
Layout
| Member | What it gives you |
|---|---|
s.W, s.H |
the width and height of the canvas |
s.cx, s.cy |
the middle of the frame. On Shorts, TikTok and Reels it is the middle of s.safe |
s.safe |
the area where content belongs, as { x, y, w, h, left, top, right, bottom }. It leaves a 7% margin and keeps out of the captions and the spots a platform app covers |
s.row(n, { width, x }) |
n x positions spread over a width |
s.col(n, { height, y }) |
n y positions spread over a height, centered in s.safe |
s.grid(cols, rows, { x, y, w, h, gap }) |
grid cells as { x, y, w, h }, row by row. Without a size, the grid fills s.safe |
s.get(id) |
the position and size of an element you drew earlier with that id |
Most elements return { x, y, w, h, left, right, top, bottom }, so you can put the next element below or beside them.
Options most elements take
| Option | What it does |
|---|---|
at, out |
when the element appears and when it leaves |
enter |
how the element comes in: draw, write, type, words, sync, pop, rise, fade, zoom, drop, slide-left, slide-right, slide-up, slide-down or none |
exit |
how it leaves: fade, pop, rise, drop, slide-left, slide-right or none |
dur |
how long the entrance takes, in seconds |
id |
a name, so that arrows, annotate and s.get can find the element |
color |
a color name like blue, accent or muted, or any CSS color |
opacity, scale, rotate |
make it see-through, change its size, or turn it. rotate is in degrees |
float |
how many pixels the element drifts slowly, so a still element does not look frozen |
sfx |
the sound it makes when it comes in, or false for no sound |
Use at: -1 for an element that was already on screen in the scene before. It is there from the start of the scene and makes no sound.
Text
s.title("How DNS works", { at: 0 });
s.subtitle("the phone book of the internet", { at: 1 });
s.text("Every site has an *address*", { y: 300, size: 64, at: "address" });
s.note("about 20 ms", { x: 1400, y: 820, at: 3 });
s.title uses the display font at 104 pixels and sits centered a little above the middle. s.subtitle is 46 pixels in the muted color. s.note is small text at 32 pixels in the muted color. All three take the same options as s.text:
| Option | What it does |
|---|---|
x, y |
where the text sits. By default this is the center of the text |
size |
the font size in pixels, 48 by default |
font |
which font: display, body, hand or mono |
weight, bold |
how heavy the font is, or bold: true |
align |
left, center or right. With left, x is the left edge |
valign |
top, middle or bottom. With top, y is the top edge |
maxWidth, lineHeight |
the width where the text wraps to a new line, and the space between lines |
color, mark |
the text color, and the color of words in *stars* |
bg, padding, radius, border |
a card behind the text, with its padding, corner radius and border |
Text has a few extra ways to come in. write writes it on the screen. type types it letter by letter, and cps sets the typing speed. words shows one word after another. sync shows each word the moment the voice says it. Words in *stars* get the accent color. \n starts a new line.
Shapes and arrows
s.box("Resolver", {
id: "res",
x: 960,
y: 540,
icon: "server",
color: "blue",
at: "resolver",
});
s.circle({ id: "you", x: 400, y: 540, r: 90, label: "You", at: 0.5 });
s.arrow("you", "res", { label: "asks", at: "asks" });
s.arrow([300, 900], [1600, 900], { bend: 0.3, dashed: true, at: 5 });
s.line(
[
[200, 800],
[900, 700],
],
{ color: "accent", at: 2 },
);
s.path("M0 0 C 200 -150 400 150 600 0", { x: 660, y: 540, at: 3 });
s.box(label, options) is a card that fits its label:
| Option | What it does |
|---|---|
w, h, minW, minH |
a fixed size, or the smallest size the box may have |
size, font, weight |
the font of the label. The size is 44 pixels by default |
icon, iconSize, iconColor |
an icon above the label, with its size and color |
color |
the color of the outline. The box gets a light fill in the same color |
fill |
your own fill color, or 'none' for no fill |
stroke, width, dashed, border |
the color of the outline, how thick it is, and whether it is dashed. border: false removes it |
radius, shape |
how round the corners are, or shape: 'ellipse' for an oval |
textColor |
the color of the label. Without it, explainroo picks a color that reads well on the fill |
fillStyle |
how the fill is drawn in the hand-drawn looks: solid, hachure, cross-hatch, zigzag or dots |
s.circle({ r, label }) takes the same options. r is the radius.
s.arrow(from, to, options) draws an arrow from one point to another. A point can be [x, y], { x, y } or the id of an element. With an id, the arrow stops at the edge of that element. bend curves the arrow, from about -1 to 1. head puts the arrow head at the end, at the start, at both ends or nowhere with none. The other options are label, labelSize, labelColor, labelOffset, gap, dashed, width and headSize. s.connect does the same thing.
s.line(points, options) draws a line through several points. s.path(d, options) draws an SVG path.
Icons and images
s.icon("database", {
x: 1500,
y: 540,
size: 140,
color: "purple",
at: "database",
});
s.icon("lock", { bg: "circle", color: "green", label: "Encrypted", at: 4 });
s.image("assets/app.png", {
w: 1100,
frame: "browser",
url: "app.example.com",
at: 0.5,
});
s.image("assets/cables.png", {
w: 1000,
frame: "card",
at: "cables",
kenburns: true,
});
There are 1,854 icons from Lucide. explainroo icons <word> searches them. Older Lucide names work too. The icon options are size (120 by default), color, weight (the line thickness, 2 by default), bg (true, 'circle', 'square' or a color, for a shape behind the icon), bgScale, label and labelSize.
s.image(src, options) draws a file from the project’s assets folder:
| Option | What it does |
|---|---|
w, h |
the size. If you give only one of them, the image keeps its shape |
fit |
cover fills the box and crops the rest, contain shows the whole image |
frame |
a frame around the image: none, card (a white border), browser, window or phone |
url |
the web address shown in the address bar of the browser frame |
radius, border, shadow |
round corners, an outline, and shadow: false to remove the shadow |
kenburns |
true for a slow zoom |
Pointing things out
s.text("It is *not* magic", { id: "claim", y: 400, at: 0 });
s.annotate("claim", { type: "underline", at: "magic" });
s.annotate(
{ x: 960, y: 700, w: 400, h: 90 },
{ type: "circle", color: "red", at: 3 },
);
s.annotate(target, options) draws a mark on an element or on an area. The target is an id or a box like { x, y, w, h }. The type of mark is underline, circle, box, highlight, strike, cross or bracket. The other options are color, padding, width and dur.
Lists, numbers and charts
s.list(["Browser cache", "Resolver", "Root server"], {
x: 360,
y: 280,
at: "first",
stagger: 1.1,
});
s.number(1500000, { suffix: " requests", at: "million", y: 480 });
s.bars(
[
{ label: "2023", value: 19 },
{ label: "2024", value: 31 },
],
{ at: 1, suffix: "k" },
);
s.lineChart([3, 5, 4, 8, 13, 21], {
labels: ["M", "T", "W", "T", "F", "S"],
at: 1,
});
s.pie(
[
{ label: "Images", value: 55 },
{ label: "Other", value: 45 },
],
{ donut: 0.55, at: 1 },
);
s.list(items, options)shows a list. Each item is text or{ text, at, icon, color, out }. For a list,xis the left edge andyis the top. The options aresize(50 by default),width,gap,bullet(dot,dash,number,check,arrowor an icon name),bulletColor,at(one start time, or one time per item),stagger(the seconds between items, 0.7 by default) andenter. Each item plays a note, and the notes go up as the list grows.s.number(value, options)counts up to a number with a ticking sound. The options arefrom,dur(1.4 seconds by default),prefix,suffix,decimals,separatorandgroup: false, plus the text options.s.bars(data, options)draws a bar chart. The options arex,y,w,h,max,stagger,growDur,values: false,prefix,suffix,format(v),labelSize,valueSizeandfillStyle. Each bar can have its owncolor.s.lineChart(values, options)draws a line chart. The options arex,y,w,h,min,max,labels,dots: false,area: false,coloranddur.s.pie(data, options)draws a pie chart. The options arex,y,r,donut(from 0 to 0.8, for a ring with a hole in the middle),labels: false,labelOffsetanddur.
Code and terminal
s.code("const res = await fetch(url);", {
lang: "js",
title: "app.js",
at: 0.5,
});
s.terminal(["$ npm install", "added 42 packages in 3s"], { at: 1 });
s.code(source, options) shows code in a window. It colors the code for js, ts, py, go, rust, php, sh, sql, json and md. The options are lang, size (32 by default), w, title (a file name, or false for no title bar), lineNumbers: false, reveal (type, lines or none), cps and lineDelay. highlight picks lines to highlight and highlightAt says when. explainroo check tells you when a line is too wide for the window.
s.terminal(lines, options) shows a terminal window. Lines that start with $ are typed in as commands. The other lines appear as output. A line can also be { cmd, at } or { out, at, color }. The options are w, size, rows, title, prompt, cps (22 by default), outputDelay and lineGap.
Camera, groups and your own drawing
s.camera([{ at, x, y, zoom, dur, rotate }])moves the view, for example to zoom in on one part of a diagram. It must be the first call in the scene function.s.group({ x, y, scale, rotate, at, out, enter }, () => { ... })moves a set of elements together. Positions inside the group count from itsxandy.s.draw({ x, y, at, out }, (ctx, life) => { ... })gives you the canvas, so you can draw anything the other methods cannot.life.pgoes from 0 to 1 while the element comes in.s.ctxis the canvas too.s.burst({ x, y, at, count, colors })throws confetti.s.bg(color)paints over the background.s.rand(i),s.noise(x)ands.wiggle(amount, speed)give random values that are the same on every render.
Sound
s.sfx(name, at, { gain, dur, pitch }) plays a sound effect at a time. The names are pop, click, whoosh, swipe, tick, type, ding, chime, thud, scribble, chalk, rise, sparkle, blip and error. dur sets how long type, scribble, chalk and rise last. A rise ends at at + dur, so start it a little before the thing it leads up to.
Colors and fonts
The color names are accent, ink, muted, bg, surface, red, orange, yellow, green, teal, blue, purple, pink and gray. Each look has its own shade of each color. So a scene keeps working when you change the look. s.color(name) gives you the CSS color. s.tint(name) gives you the light version of it that is used for fills.
There are four fonts. display is for titles, body for normal text, hand for handwriting and mono for code.