Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Shapes — the cast

Everything on screen is an entity with a name (its first argument). You declare shapes once; the name is how you address them later in the script.

The six primitives

Each line below is the whole call — copy it and tweak the numbers.

shapewritedraws
circlecircle(sun, (cx, cy), 90);a circle, radius 90, at the centre
rectrect(box, (cx, cy), 200, 120);a rectangle 200 wide, 120 tall
lineline(edge, (100, 100), (400, 300));a line from point to point
arrowarrow(v, (100, 400), (400, 400));a line with an arrowhead at the end
dotdot(p, (cx, cy), 8);a small filled dot, radius 8
texttext(cap, (cx, 640), "hello");a text label anchored at a point

Ordinary text is portable and shaped automatically

text, say, captions, labels, and kit-generated words use Manic’s embedded font engine. No machine font is consulted, and there is no font-selection DSL: choose only the semantic mono, bold, or display look. Manic handles fallback, ligatures, combining marks, bidirectional order, wrapping, reveal, rotation, glow, camera zoom, and recording from one shaped layout.

Arabic and Devanagari are bundled script examples, so these work unchanged in preview, backend recording, and the browser build:

text(arabic, (cx, 300), "التعلّم يجعل الأفكار واضحة");
text(hindi,  (cx, 420), "ज्ञान से प्रकाश मिलता है");

Use equation or inline $…$ for mathematical typesetting. Run manic check before rendering; an unsupported Unicode cluster names the entity and code point instead of silently drawing a replacement box. Colour emoji remains intentionally unsupported until colour-font rendering is portable.

Plus a few composite helpers built from those: polygon, arc/sector, brace/ bracelabel, caption (a row of words), and support(id, (cx,cy), [len], ["dir"]) — the hatched wall / ceiling / floor for mechanics & textbook diagrams ("dir" is the open side: "down" ceiling, "up" floor, "left"/"right" walls).

Two more helpers cover scenes that would otherwise need dozens of manual dots and endpoints:

circle(glass, (400,300), 100);
particles(bubbles, glass, 24, 5, 7); // default random layout

rect(tank, (750,300), 180, 160);
particles(samples, tank, 24, 4, 11, "grid");
link(pipe, glass, tank, 35);         // 35 px of curve; 0 is straight

circle(orbit, (980,300), 90);
particles(ticks, orbit, 24, 4, 13, "ring");

particles works inside circles and rectangles. Its bare id addresses every child dot, so hidden(bubbles) and recolor(bubbles, fg) affect the whole group. Use the optional "grid" layout for an ordered rectangular state, then arrange(samples, tank, "random", 2, smooth) to move the same dots into a typical random state. arrange may target a larger container for expansion and can return to "grid" exactly. A circle plus "ring" gives the particles an ordered radial endpoint. link meets shapes at their boundaries and stays attached when either endpoint moves.

Points are (x, y) in pixels, origin top-left, y increasing downward. Use cx, cy, w, h to stay canvas-independent.

For display mathematics, equation accepts LaTeX in backticks. Standard \textcolor can emphasize individual terms with Manic’s semantic palette; the colors automatically follow the selected template:

equation(step, (cx, 620),
  `\textcolor{magenta}{\mathrm{slope}}=
   \frac{\textcolor{cyan}{\mathrm{rise}}}
        {\textcolor{gold}{\mathrm{run}}}`, 40);

Leave a term uncolored to use the template foreground. A single-color equation can still be styled as one entity with color and animated with recolor.

For a derivation, keep that same equation id and animate only the change:

rewrite(step, `\mathrm{slope}=\frac{\mathrm{rise}}{\mathrm{run}}`, 0.9, smooth);
rewrite(step, `\mathrm{slope}=\frac{x}{1}`, 0.9, smooth);

Manic matches the rendered LaTeX parts; it does not perform algebra. This makes the verb generic across algebra, calculus, physics, sums, fractions, radicals, and creator scenes while keeping each settled formula exact.

// the six primitive shapes, drawn on together.
title("Shapes");
canvas("16:9");

text(t, (cx, 90), "six primitives");  color(t, cyan);  size(t, 32);  hidden(t);

circle(c, (240, 380), 80);            color(c, cyan);     stroke(c, 4);  untraced(c);
rect(r,   (470, 380), 150, 150);      color(r, magenta);  stroke(r, 4);  untraced(r);
line(l,   (640, 300), (820, 460));    color(l, lime);     stroke(l, 4);  untraced(l);
arrow(a,  (900, 460), (1040, 300));   color(a, cyan);     stroke(a, 4);  untraced(a);
dot(d,    (1110, 380), 12);           color(d, magenta);  hidden(d);
text(lbl, (640, 620), "circle · rect · line · arrow · dot · text");
color(lbl, dim);  size(lbl, 24);  hidden(lbl);

show(t, 0.5);
par { draw(c);  draw(r);  draw(l);  draw(a);  show(d); }
show(lbl, 0.5);
wait(1.2);

▶ See it play:

Modifiers — style a shape at t = 0

A shape starts plain. Modifiers change how it looks before the animation begins. They take the entity name first, then a value:

modifiereffectexample
color(id, c)fill / stroke colourcolor(sun, cyan);
stroke(id, w)line thicknessstroke(sun, 4);
dashed(id, [dash], [gap])dashed path (defaults 16/10 px)dashed(curve, 18, 10);
size(id, n)text sizesize(cap, 30);
glow(id, n)neon halo strengthglow(sun, 8);
opacity(id, 0..1)transparencyopacity(sun, 0.5);
filled(id) / outlined(id)turn fill / outline onfilled(box);
hue(id, deg)colour by an angle (0–360) — for gradients & loopshue(seg, 200);
z(id, n)draw order (higher = on top)z(box, 5);
sticky(id)pin to the screen so it stays put through a cam/zoom (a HUD)sticky(caption);

And two that decide how a shape first appears:

modifierpairs withgives
hidden(id)show(id)a fade-in
untraced(id)draw(id)a draw-on (pen tracing the outline)

Colours are a fixed palette: fg, void, cyan, magenta, lime, dim, panel. For a computed colour (say, one per item in a loop) use hue(id, degrees). More in Colour & style.

Naming things in a loop

When you make many shapes with a for loop, give each a unique name with interpolation{expr} glued to the name:

for i in 0..5 {
  dot(p{i}, (200 + i*180, cy), 8);   // p0, p1, p2, p3, p4
}

That’s your cast. Now let’s make it move → Verbs.