Skip to content
explainroo

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, x is the left edge and y is the top. The options are size (50 by default), width, gap, bullet (dot, dash, number, check, arrow or an icon name), bulletColor, at (one start time, or one time per item), stagger (the seconds between items, 0.7 by default) and enter. 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 are from, dur (1.4 seconds by default), prefix, suffix, decimals, separator and group: false, plus the text options.
  • s.bars(data, options) draws a bar chart. The options are x, y, w, h, max, stagger, growDur, values: false, prefix, suffix, format(v), labelSize, valueSize and fillStyle. Each bar can have its own color.
  • s.lineChart(values, options) draws a line chart. The options are x, y, w, h, min, max, labels, dots: false, area: false, color and dur.
  • s.pie(data, options) draws a pie chart. The options are x, y, r, donut (from 0 to 0.8, for a ring with a hole in the middle), labels: false, labelOffset and dur.

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 its x and y.
  • s.draw({ x, y, at, out }, (ctx, life) => { ... }) gives you the canvas, so you can draw anything the other methods cannot. life.p goes from 0 to 1 while the element comes in. s.ctx is 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) and s.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.