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 — filled + outlined by default |
| rect | rect(box, (cx, cy), 200, 120); | a rectangle — filled + outlined by default |
| line | line(edge, (100, 100), (400, 300)); | a line from point to point (stroke only) |
| arrow | arrow(v, (100, 400), (400, 400)); | a line with an arrowhead at the end (stroke only) |
| dot | dot(p, (cx, cy), 8); | a small filled disc (no outline) — hides crossings |
| text | text(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:
| 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 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 strokes | gradient(wave, blue, cyan, gold, 270); |
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) — per-entity colour in 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); |
Textbook diagrams: prefer
outlined(or a thinarc) for angle marks and “regions of interest.” Afilleddisc 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.
circledefaults to filled + outlined (neon UI). For math constructions always pairoutlined(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), notdot— a solid marker paints over the intersection you are trying to show.
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,gold,red,orange,blue,teal,violet,coral,indigo,mint,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.