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 — filled + outlined by default
rectrect(box, (cx, cy), 200, 120);a rectangle — filled + outlined by default
lineline(edge, (100, 100), (400, 300));a line from point to point (stroke only)
arrowarrow(v, (100, 400), (400, 400));a line with an arrowhead at the end (stroke only)
dotdot(p, (cx, cy), 8);a small filled disc (no outline) — hides crossings
texttext(cap, (cx, 640), "hello");a text label anchored at a point

Plus composites: polygon / sector / annulus / pie (filled by default), arc / brace / ellipse (stroke only), and boolean regions (union / intersect / …).

▶ See it play: SHAPECRAFT — fill vs outlined, the cast, palette → become, spin-wound morph, a disc that splits into four colours → reunites → moves, and two-colour booleans.

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 or outlined circle (defaults 16/10 px)dashed(mark, 4, 3);
gradient(id, c1, c2, ..., [mode])multi-stop gradient: along a path stroke, a linear (angle°) / radial fill, or a computed "speed"/"curvature" quantity on strokesgradient(wave, blue, cyan, gold, 270);
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) — per-entity colour in 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);

Textbook diagrams: prefer outlined (or a thin arc) for angle marks and “regions of interest.” A filled disc or sector sitting on top of axes and construction lines hides what’s underneath — even at low opacity. Reserve solid fills for backdrop wells behind the diagram, not overlays.

circle defaults to filled + outlined (neon UI). For math constructions always pair outlined(id)outline(id, color) alone only recolors the rim and leaves the fill on. Contact points that land on crossings should be open / dashed rings (outlined + dashed), not dot — a solid marker paints over the intersection you are trying to show.

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, gold, red, orange, blue, teal, violet, coral, indigo, mint, 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.