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.
| shape | write | draws |
|---|---|---|
| circle | circle(sun, (cx, cy), 90); | a circle, radius 90, at the centre |
| rect | rect(box, (cx, cy), 200, 120); | a rectangle 200 wide, 120 tall |
| line | line(edge, (100, 100), (400, 300)); | a line from point to point |
| arrow | arrow(v, (100, 400), (400, 400)); | a line with an arrowhead at the end |
| dot | dot(p, (cx, cy), 8); | a small filled dot, radius 8 |
| text | text(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:
| modifier | effect | example |
|---|---|---|
color(id, c) | fill / stroke colour | color(sun, cyan); |
stroke(id, w) | line thickness | stroke(sun, 4); |
dashed(id, [dash], [gap]) | dashed path (defaults 16/10 px) | dashed(curve, 18, 10); |
size(id, n) | text size | size(cap, 30); |
glow(id, n) | neon halo strength | glow(sun, 8); |
opacity(id, 0..1) | transparency | opacity(sun, 0.5); |
filled(id) / outlined(id) | turn fill / outline on | filled(box); |
hue(id, deg) | colour by an angle (0–360) — for gradients & loops | hue(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:
| modifier | pairs with | gives |
|---|---|---|
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) usehue(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.