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

Coming from Manim

For folks arriving from Manim: each of these is a well-known Manim tutorial scene written the Manic way — same idea, Manic’s vocabulary, usually far less code. The side-by-side translation guide is in Coming from Manim to Manic; these are the runnable files.

Each block is the whole file — copy it into x.manic and run manic x.manic (live) or --record out (video).

graph-area-plot

A coords frame + two plots, a vline, riemann bars, and a band between curves — Manim’s GraphAreaPlot, static.

// ============================================================================
//  GraphAreaPlot — the Manim tutorial scene, in Manic.
//
//  A coordinate frame with two parabolas, vertical lines up to the first curve,
//  Riemann rectangles under it, and a shaded region between the two curves.
//
//  Coordinate frame: math origin (0,0) → screen (250,620); 130 px per x-unit,
//  92 px per y-unit. Every plot below shares that same origin + scale.
// ============================================================================
title("Manim vs Manic — GraphAreaPlot");
canvas(1280, 720);
template("black");

// axes over x∈[0,5], y∈[0,6] — no arrow tips, ticks numbered, axes named "x" / "y"
coords(ax, (250, 620), (0, 5), (0, 6), 130, 92, 0, 1, 1, "x", "y");

// the two curves
plot(c1, (250, 620), 130, 92, "4*x - x*x", (0, 4));          color(c1, blue);  stroke(c1, 3);   // 4x − x²
plot(c2, (250, 620), 130, 92, "0.8*x*x - 3*x + 4", (0, 4));  color(c2, green); stroke(c2, 3);   // 0.8x² − 3x + 4

// dotted guide lines from the x-axis up to curve_1 at x = 2 and x = 3
vline(l1, c1, 2, yellow);
vline(l2, c1, 3, yellow);

// Riemann rectangles under curve_1 on [0.3, 0.6], bars of width 0.03
riemann(rm, c1, 0.3, 0.6, 0.03, green);

// the region between the two curves on the slice [2, 3] (curve_1 on top)
band(ar, c1, c2, grey, (2, 3)); opacity(ar, 0.5);

graph-area-plot-animated

The same curve toured live: a sliding tangent+slope, an area sweep + integral readout, re-selected domains, then riemann bars.

// ============================================================================
//  GraphAreaPlot — the full tour. One curve, y = 4x − x², and the whole calculus
//  toolkit moving over it, selecting different domains as it goes:
//
//    1. reveal the frame + curve
//    2. DERIVATIVE — a tangent slides along the curve, slope read live
//    3. INTEGRAL   — the area sweeps open 0→4, the value climbs to 32/3
//    4. DOMAINS    — the interval is re-selected: [0,1], then [1,3], then [3,4]
//    5. RIEMANN    — approximate the selected [1,3] with rectangles
//
//  Same declarative primitives as the static diagram — the animator only says
//  what happens when.
// ============================================================================
title("GraphAreaPlot — the full tour");
canvas(1280, 720);
template("black");

coords(ax, (250, 620), (0, 5), (0, 6), 130, 92, 0, 1, 1, "x", "y");
plot(c1, (250, 620), 130, 92, "4*x - x*x", (0, 4)); color(c1, blue); stroke(c1, 3);

// tour props
tangent(tg, c1, 0.4, 150); color(tg, gold); stroke(tg, 2);
slope(sv, c1, 0.4, (22, -28)); color(sv, gold); size(sv, 24);
area(acc, c1, 0, 0); color(acc, cyan);                       // whole-domain area, sweeps
integral(iv, c1, 0, 0, (940, 150)); color(iv, cyan); size(iv, 30);
area(a01, c1, 0, 1); color(a01, cyan);                       // the three selected sub-domains
area(a13, c1, 1, 3); color(a13, gold);
area(a34, c1, 3, 4); color(a34, magenta);
riemann(rm, c1, 1, 3, 0.25, green);                          // Riemann on the chosen [1,3]

// initial states
hidden(ax); untraced(c1);
hidden(tg); hidden(sv); hidden(iv);
opacity(acc, 0.30);
opacity(a01, 0); opacity(a13, 0); opacity(a34, 0); opacity(rm, 0);

// ===== 1. reveal the frame + curve =====
show(ax, 0.7); wait(0.2);
draw(c1, 1.2); wait(0.4);

// ===== 2. DERIVATIVE — the tangent slides, its slope read live (4 → 0 → −4) =====
par { show(tg, 0.4); show(sv, 0.4); }
par { to(tg, x, 3.6, 3.2, smooth); to(sv, x, 3.6, 3.2, smooth); }
wait(0.3);
par { fade(tg, 0.4); fade(sv, 0.4); }

// ===== 3. INTEGRAL — the area sweeps open, the value climbs to 32/3 ≈ 10.67 =====
show(iv, 0.4);
par { to(acc, x, 4, 3.2, smooth); to(iv, x, 4, 3.2, smooth); }
wait(0.7);
par { fade(acc, 0.5); fade(iv, 0.4); }

// ===== 4. DIFFERENT DOMAINS — re-select the interval, one slice at a time =====
to(a01, opacity, 0.5, 0.5); wait(0.5);       // [0, 1]
to(a13, opacity, 0.5, 0.5); wait(0.5);       // [1, 3]
to(a34, opacity, 0.5, 0.5); wait(0.7);       // [3, 4]
par { fade(a01, 0.5); fade(a34, 0.5); }      // keep just [1, 3]

// ===== 5. RIEMANN — approximate the selected [1,3] with rectangles =====
par { fade(a13, 0.5); to(rm, opacity, 0.75, 0.7); }
wait(0.9);

custom-axis-labels

A radian sine on coords with xtick/ytick labelling ticks at an axis VALUE (π/2) and named axes — no pixel maths.

// ============================================================================
//  Custom (non-numeric) axis + tick labels — no pixel maths anywhere.
//
//  `coords` names the axes for you (xname / yname), and `xtick` / `ytick` place
//  a label at an axis VALUE — the label is any text: a number, a word, or a
//  symbol like "π/2". Here: a sine wave on a radian axis.
// ============================================================================
title("Custom axis labels");
canvas(1280, 720);
template("black");

// frame: x from 0 to ~2π, y from -1.4 to 1.4; ticks every π/2; named axes
coords(ax, (200, 380), (0, 6.6), (-1.4, 1.4), 150, 130, 0, 1.5708, 0, "θ", "sin θ");
plot(sine, (200, 380), 150, 130, "sin(x)", (0, 6.2832)); color(sine, cyan); stroke(sine, 3);

// x-axis ticks labelled in radians — text placed at each VALUE, not a pixel
xtick(qa, ax, 1.5708, "π/2");
xtick(qb, ax, 3.1416, "π");
xtick(qc, ax, 4.7124, "3π/2");
xtick(qd, ax, 6.2832, "2π");

// y-axis: just the extremes
ytick(yp, ax, 1, "1");
ytick(ym, ax, -1, "-1");

constant-area-rectangle

A rectangle rides y=25/x via boxto+mark; the corner slides but the area stays 25 — Manim’s always_redraw, without the updater.

// ============================================================================
//  Constant-area rectangle under a hyperbola.
//
//  A rectangle runs from the origin to a point (x, 25/x) on the curve y = 25/x.
//  As the corner slides along the curve, the rectangle morphs from wide-and-short
//  to tall-and-narrow — but its area (width × height = x · 25/x) stays fixed at 25.
//  A dot rides the corner along the curve.
// ============================================================================
title("Constant-area rectangle");
canvas(1280, 720);
template("black");

coords(ax, (200, 640), (0, 10), (0, 10), 90, 55, 0, 2, 1, "x", "y");
plot(g, (200, 640), 90, 55, "25/x", (2.5, 10)); color(g, gold); stroke(g, 3);

boxto(box, g, 5); color(box, blue);       // rectangle origin → (x, 25/x); area = x·(25/x)
mark(dot, g, 5); color(dot, fg);          // a dot riding the corner along the curve
equation(inv, (760, 150), `x \cdot y = 25`, 30); color(inv, blue);

// the axes, curve and dot are up from the start; the box is CREATED (drawn into being)
untraced(box); hidden(inv);

wait(0.4);
draw(box, 1.3);                                        // outline traces on as the fill rises bottom-up
wait(0.3);
show(inv, 0.5);                                        // then reveal the invariant
wait(0.4);
// slide the corner along the curve — width and height trade off, area holds at 25
par { to(box, x, 10, 1.8, smooth);  to(dot, x, 10, 1.8, smooth);  }   // wide & short
par { to(box, x, 2.5, 2.0, smooth); to(dot, x, 2.5, 2.0, smooth); }   // tall & narrow
par { to(box, x, 5, 1.8, smooth);   to(dot, x, 5, 1.8, smooth);   }   // back to the 5×5 square
wait(0.6);

moving-camera-follow

A dot walks a sine; followshot makes the 2D camera track it, then releases and restores the wide shot — Manim’s MovingCameraScene.

// ============================================================================
//  A moving camera that follows a dot along a curve.
//
//  The wide shot shows a sine wave with dots at both ends. The camera zooms in
//  on a dot at the start, then TRACKS it as it travels the whole curve — a follow
//  shot — before pulling back out to the wide shot.
// ============================================================================
title("Moving camera — follow shot");
canvas(1280, 720);
template("black");

coords(ax, (200, 480), (-1, 10), (-1, 10), 95, 45, 1, 2, 1, "x", "y");
plot(g, (200, 480), 95, 45, "sin(x)", (0, 9.4248)); color(g, blue); stroke(g, 3);

mark(d1, g, 0);        color(d1, fg);        // fixed dot at the start
mark(d2, g, 9.4248);   color(d2, fg);        // fixed dot at the end
mark(md, g, 0);        color(md, orange);    // the dot the camera will follow

wait(0.6);
// zoom in and glide to the dot at the start of the curve
par { zoom(2, 1.3, smooth); cam((200, 480), 1.3, smooth); }
followshot(md);                              // lock the camera onto the moving dot
to(md, x, 9.4248, 4.5, linear);              // it walks the whole sine curve; the camera trails it
followshot(none);                            // release the lock
// pull back out to the wide shot
par { zoom(1, 1.3, smooth); cam((640, 360), 1.3, smooth); }
wait(0.5);

moving-highlight-box

The product rule written with mathparts (addressable terms), a framebox boxes one term, then surround glides it to the next.

// ============================================================================
//  The product rule, with a highlight box that moves between the two terms.
//
//  The equation is one `mathparts` — split into parts that lay themselves out and
//  stay individually addressable (pr.0, pr.1, …). It's written on left-to-right,
//  a box is drawn around the first product term, then the box glides + resizes to
//  surround the second.
// ============================================================================
title("Product rule — moving highlight box");
canvas(1280, 720);
template("black");

mathparts(pr, (640, 340),
  `\frac{d}{dx}f(x)g(x)=`, `f(x)\frac{d}{dx}g(x)`, `+`, `g(x)\frac{d}{dx}f(x)`, 34);
framebox(fb, pr.1, 10); color(fb, gold);        // box the first product term (pr.1)

untraced(pr); untraced(fb);

wait(0.5);
// write the equation on, part by part, left to right
draw(pr.0, 0.9); draw(pr.1, 0.7); draw(pr.2, 0.3); draw(pr.3, 0.8);
wait(0.4);
draw(fb, 0.8);                                  // draw the box around the first term
wait(0.9);
surround(fb, pr.3, 1.0, smooth);                // move it to the second term (pr.3)
wait(0.8);

moving-angle

Two arms share a vertex; one swings and anglemark’s arc + its θ label track the opening live, then the label recolours — Manim’s MovingAngle.

// ============================================================================
//  A moving angle: two arms share a vertex, one arm swings, and the angle arc
//  plus its θ label track the opening live — no updaters, just `turn`.
//
//  The whole rig is reactive: `segment` arms reflow to their endpoints, and
//  `anglemark` recomputes its arc AND rides its θ label along the bisector
//  every frame. Rotate the moving point and everything follows.
// ============================================================================
title("Moving angle");
canvas(1280, 720);
template("black");

point(o,  (470, 470));           // vertex — the pivot everything turns about
point(pr, (800, 470));           // fixed arm, along the horizontal
point(pm, (357, 188));           // moving arm, starts at ~110°

segment(l1, o, pr);
segment(lm, o, pm);
anglemark(ang, pr, o, pm, "θ");  // arc + θ label, both track the opening

wait(0.6);
turn(pm, o, -70, 1.6);           // close the angle down to ~40°
wait(0.5);
turn(pm, o, 140, 2.2);           // swing wide, past straight
wait(0.4);
recolor(ang.label, red, 0.5);    // paint the θ red
wait(0.4);
turn(pm, o, 170, 2.6);           // carry on around
wait(0.6);

moving-dots

Two dots move on independent axes (slidex/slidey) joined by a segment that always connects them — Manim’s MovingDots.

// ============================================================================
//  Two dots move on independent axes, joined by a line that always connects
//  them. The blue dot slides right; the green dot rises; the red segment
//  stretches to follow — reactively, with no updaters.
//
//  `slidex`/`slidey` push one dot along a single axis (the other coordinate
//  stays put), and `segment` reflows to its endpoints every frame.
// ============================================================================
title("Moving dots");
canvas(1280, 720);
template("black");

point(a, (440, 420));            // blue dot — will slide right
point(b, (640, 420));            // green dot — will rise
color(a, blue);
color(b, green);

segment(l, a, b);                // the joining line, tracks both endpoints
color(l, red);

wait(0.5);
slidex(a, 840, 1.5);             // blue dot travels right along its row
wait(0.3);
slidey(b, 180, 1.5);             // green dot climbs its column
wait(0.6);

moving-group-to-destination

groupscale grows a tagged row about its centre, then dock slides it so one member lands on a target — Manim’s MovingGroupToDestination.

// ============================================================================
//  A whole group of dots slides to a destination — rigidly, so that one chosen
//  member (the red dot) lands exactly on a yellow target, carrying the rest of
//  the row along with it.
//
//  The four dots share a tag, so the whole row moves and scales as one:
//  `groupscale` grows it about its collective centre, then `dock` slides it so
//  the chosen member (the red dot) lands on the target. You name the member and
//  the target; the shift is worked out for you — no delta arithmetic.
// ============================================================================
title("Moving group to destination");
canvas(1280, 720);
template("black");

point(d0, (360, 470));  tag(d0, row);
point(d1, (470, 470));  tag(d1, row);
point(d2, (580, 470));  tag(d2, row);   // the chosen member
point(d3, (690, 470));  tag(d3, row);
color(d2, red);

point(dest, (940, 190));
color(dest, yellow);

wait(0.6);
groupscale(row, 1.4, 1.0);   // grow the row about its own centre
wait(0.3);
dock(row, d2, dest, 1.6);    // slide the row so d2 lands on dest
wait(0.5);

point-with-trace

A dot swings a half-circle then steps up/left; trail records the whole path behind it — sampled from the timeline, so it’s exact and seekable.

// ============================================================================
//  A dot leaves a trail as it moves — first swinging a half-circle around a
//  pivot, then stepping up and to the left. The gold path records everywhere
//  the dot has been, growing behind it as it goes.
//
//  `trail` samples the dot's trajectory once and replays it by time, so the
//  path is exact and seekable — no per-frame bookkeeping.
// ============================================================================
title("Point with trace");
canvas(1280, 720);
template("black");

point(dot, (540, 380));
trail(tr, dot, gold, 3);

wait(0.4);
turn(dot, (740, 380), 180, 2.0, linear);   // swing a half-circle about the pivot
wait(0.4);
shift(dot, (0, -160), 1.0);                 // step up
shift(dot, (-200, 0), 1.0);                 // step left
wait(0.6);

sin-and-cos-plot

sin & cos on one frame with graphlabel LaTeX labels pinned to each curve, plus a vline guide at x=2π — Manim’s SinAndCosFunctionPlot.

// ============================================================================
//  sin and cos on one coordinate frame, each curve labelled where it lives, plus
//  a vertical guide at x = 2π with its own label.
//
//  `coords` gives the whole frame (ranges, ticks, numbers, axis names); the new
//  piece is `graphlabel` — a LaTeX label that pins itself to a curve at an x and
//  nudges clear of the line, taking the curve's colour by default.
// ============================================================================
title("sin and cos");
canvas(1280, 720);
template("black");

coords(ax, (640, 360), (-10, 10), (-1.5, 1.5), 52, 105, 0, 2, 1, "x", "y");

plot(sinq, (640, 360), 52, 105, "sin(x)", (-10, 10));  color(sinq, blue);
plot(cosq, (640, 360), 52, 105, "cos(x)", (-10, 10));  color(cosq, red);

graphlabel(sl, sinq, `\sin(x)`, -9, up);      // rides sin near its left end
graphlabel(cl, cosq, `\cos(x)`);              // default: cos's right end, up

vline(vl, cosq, 6.2832, yellow);              // guide up to cos at x = 2π
graphlabel(ll, cosq, `x=2\pi`, 6.2832, upright, white);

moving-zoomed-scene

A loupe: a frame over the scene + a panel showing that region magnified LIVE; pan the frame and the panel tracks it — Manim’s ZoomedScene.

// ============================================================================
//  A magnifier (loupe): a small frame sits over a detailed corner of the scene,
//  and a panel shows that region blown up live. Move the frame and the panel
//  tracks it — the magnified view is the real scene, redrawn zoomed.
// ============================================================================
title("Moving zoomed scene");
canvas(1280, 720);
template("black");

// --- a patch of fine detail to inspect ---
circle(a, (360, 300), 10);  color(a, red);
circle(b, (400, 290), 7);   color(b, cyan);
circle(c, (438, 306), 9);   color(c, green);
circle(d, (410, 330), 5);   color(d, gold);
text(cap, (400, 360), "detail", 16);
dot(spot, (400, 300), 4);

// --- the magnifier: frame over the patch, panel showing it 3.2x ---
loupe(lp, (400, 310), 150, 96, (900, 360), 3.2);

wait(0.4);
show(lp.panel);
wait(0.4);
shift(lp.frame, (-40, -30), 1.4);   // pan the loupe — the panel follows live
wait(0.4);
shift(lp.frame, (60, 40), 1.4);
wait(0.6);

deform-homotopy

deform — a continuous homotopy: over its duration it remaps a shape’s outline by (u(x,y,t), v(x,y,t)) each frame. A wave ripples through a circle, then a twist shears a square and unwinds — Manim’s Homotopy/ApplyWave/ComplexHomotopy in one verb (the animated twin of warp).

// deform — a continuous homotopy: over its duration, `deform` animates t: 0→1 and
// remaps an entity's outline points by (u(x,y,t), v(x,y,t)) every frame. Manim's
// `Homotopy` / `ApplyWave` / `ComplexHomotopy` in one verb (the animated twin of
// the static `warp`). Write t=0 as the identity (`u=x`, `v=y`) for a clean start.
//
//   manic examples/deform-homotopy.manic
title("Deform — a continuous homotopy");
canvas("16:9");
template("black");

circle(ring, (380, 330), 140); color(ring, cyan);
rect(box, (940, 330), 230, 230); color(box, gold);
text(narr, (640, 636), "One verb, any point-map of (x, y, t).", 24);

wait(0.6);

say(narr, "A wave ripples through a shape — v = y + amp·sin(x·k + t)·sin(π·t).");
deform(ring, "x", "y + 34*sin(x*0.05 + t*9)*sin(3.14159*t)", 2.8, smooth);
wait(1.6);

say(narr, "A twist — shear that grows from centre, then unwinds. Same verb.");
deform(box, "x + (y-330)*0.45*sin(3.14159*t)", "y - (x-940)*0.45*sin(3.14159*t)", 2.8, smooth);
wait(2.0);

say(narr, "Author the identity at t=0, and any continuous deformation follows.");
wait(2.0);

first-class-shapes

Every core shape owns its FILL and its OUTLINE — each with its own colour and opacity — and DRAWS ITSELF ON. Circle, rect, polygon, ellipse, line, arrow: solid, hollow (outlined), or a translucent fill under a crisp rim (opacity(id, v, fill)), each revealed with untraced+draw — Manim’s stylable Mobject + Create, native.

// First-class shapes — EVERY core Manic shape owns its FILL and its OUTLINE (each
// its own colour + opacity) and DRAWS ITSELF ON. Not just circles: rect, polygon,
// ellipse, line, arrow — all the same first-class treatment. All native Manic: the
// ctors are unchanged, style is the usual t=0 modifiers, the reveal is untraced+draw.
//
//   color(id, c)         → the FILL colour       outline(id, c) → the RING colour
//   opacity(id, v, fill) → the FILL opacity      opacity(id, v, stroke) → the RING opacity
//   outlined(id)         → hollow (fill off)     filled(id) → solid (fill on)
//   untraced(id); draw(id) → the shape draws itself on (Manim's Create)
//
// Closed shapes (circle/rect/polygon/ellipse) have a fill you can make translucent
// or drop; open paths (line/arrow) are stroke-only but still draw themselves on.
//
//   manic examples/manim-vs/first-class-shapes.manic
title("First-class shapes");
canvas("16:9");
template("black");

// ── closed shapes: fill + outline, three postures ───────────────────────────
// SOLID filled — draws on, fill washes in behind the pen.
circle(cc, (220, 250), 72); color(cc, cyan);
untraced(cc);

// HOLLOW ring — outlined() drops the fill; outline() colours the ring.
rect(rc, (490, 250), 160, 120); outline(rc, gold); outlined(rc);
untraced(rc);

// TRANSLUCENT fill under a crisp rim — opacity(id,v,fill) touches only the interior.
polygon(tri, (720, 185), (802, 320), (638, 320)); color(tri, magenta); outline(tri, white);
opacity(tri, 0.45, fill); opacity(tri, 1.0, stroke);
untraced(tri);

// ELLIPSE is a stroke-only curve (no fill layer), so `color` sets its stroke — a
// naturally hollow conic. Draws on like any path.
ellipse(el, (1010, 250), 96, 60, 0); color(el, lime); stroke(el, 3);
untraced(el);

// ── open paths: stroke-only, but they draw on too ───────────────────────────
line(seg, (170, 470), (440, 470)); color(seg, cyan); stroke(seg, 3);
untraced(seg);

arrow(arw, (560, 510), (860, 430)); color(arw, gold);
untraced(arw);

text(cap, (640, 610), "every shape owns its fill + outline, and draws itself on", 22);
hidden(cap);

// ── timeline: verbs only. Watch each shape create. ──────────────────────────
wait(0.4);
show(cap);
wait(0.8);

say(cap, "Closed shapes: solid, hollow, translucent fill — each drawn on.");
draw(cc);
wait(0.5);
draw(rc);
wait(0.5);
draw(tri);
wait(0.5);
draw(el);
wait(1.2);

say(cap, "Open paths draw on too — a line, an arrow.");
par { draw(seg); draw(arw); }
wait(1.2);

// once drawn they're ordinary styled shapes — every verb still works (kit rule).
say(cap, "Then the usual verbs keep working — pulse, flash.");
par { pulse(tri); flash(cc); }
wait(1.4);

show-styles

The reveal family on one page: a plain show fade-in, a grow-in from an anchor (hidden(id, from)show), and a pen draw-on (untraced+draw) — how a shape first appears is a base-setter + verb pair, not a special effect.

// The show family — reveal styles. `show` brings an entity onto the scene; the
// STYLE is chosen by how you `hidden` it first (the base-setter), exactly like
// `untraced` arms `draw`. Plain `hidden(id)` → fade-in; `hidden(id, from)` arms a
// GROW-IN from an anchor: `center` | `up`/`down`/`left`/`right` (a bbox edge) |
// `tail` (an arrow's start) | a point `(x,y)`. Manim's FadeIn + GrowFromCenter/
// Edge/Arrow/Point, all under one reveal verb.
//
//   manic examples/manim-vs/show-styles.manic
title("The show family — reveal styles");
canvas("16:9");
template("black");

circle(s1, (220, 300), 58); color(s1, cyan);
circle(s2, (450, 300), 58); color(s2, lime);
rect(s3, (680, 300), 116, 116); color(s3, gold);
arrow(s4, (880, 360), (1020, 235)); color(s4, magenta);
circle(s5, (1150, 300), 58); color(s5, #ff8c1a);
text(narr, (640, 600), "The show family — one verb, many reveal styles.", 24);

// how you HIDE it picks the reveal style:
hidden(s1);              // plain           → fade-in         (FadeIn)
hidden(s2, center);      // from centre     → pop in          (GrowFromCenter)
hidden(s3, up);          // from top edge   → unfold          (GrowFromEdge)
hidden(s4, tail);        // from arrow tail → extend          (GrowArrow)
hidden(s5, (1150, 520)); // from a point    → fly + grow      (GrowFromPoint)
hidden(narr);

wait(0.4);
show(narr);
wait(1.6);

say(narr, "show — a fade-in. (Manim FadeIn)");
show(s1, 0.7);
wait(1.5);

say(narr, "hidden(center) + show — grow from the centre. (GrowFromCenter)");
show(s2, 0.7);
wait(1.6);

say(narr, "hidden(up) — unfold from a bounding edge. (GrowFromEdge)");
show(s3, 0.7);
wait(1.6);

say(narr, "hidden(tail) on an arrow — grow from the tail to the tip. (GrowArrow)");
show(s4, 0.7);
wait(1.6);

say(narr, "hidden((x,y)) — grow from any point. (GrowFromPoint)");
show(s5, 0.8);
wait(1.8);

say(narr, "The style lives on `hidden` — like `untraced` arms `draw`.");
wait(2.0);

indication

The attention flourishes — pulse, flash, wiggle, blink, circumscribe, spotlight, passflash — each a temporary emphasis that returns the shape to rest, on any entity or tagged group (Manim’s Indicate / Flash / Circumscribe / FocusOn family).

// Attention cues — ways to draw the eye to an object, each returning to its base
// state. Alongside the existing `pulse` (a scale bump) and `flash` (a radial
// burst), this adds `wiggle` (scale + rotate jiggle), `circumscribe` (a line
// drawn round it, then gone), `spotlight` (a disc converges onto it), `blink`,
// and `passflash` (a light travels the outline).
// Manim's Indicate / Flash / Wiggle / Circumscribe / FocusOn / Blink / ShowPassingFlash.
//
//   manic examples/manim-vs/indication.manic
title("Attention cues — indicate an object");
canvas("16:9");
template("black");

circle(a, (240, 320), 56); color(a, cyan);
rect(b, (520, 320), 112, 112); color(b, gold);
circle(c, (800, 320), 56); color(c, lime);
equation(d, (1050, 315), `E=mc^2`, 30); color(d, magenta);
text(narr, (640, 560), "Five ways to draw the eye — each returns to base.", 24);

hidden(narr);
wait(0.5);
show(narr);
wait(1.4);

say(narr, "wiggle — a scale-and-rotate jiggle (the rotate cousin of shake).");
wiggle(a, 0.8);
wait(1.4);

say(narr, "circumscribe — a line drawn round it, then gone.");
circumscribe(b, magenta, 1.1);
wait(1.4);

say(narr, "spotlight — a translucent disc converges onto it.");
spotlight(c, 1.4);
wait(1.4);

say(narr, "blink — flash out and in.");
blink(d, 0.8);
wait(1.4);

say(narr, "passflash — a light travels the outline.");
passflash(b, cyan, 1.3);
wait(1.6);

spiral-cycle-restore

Three position moves: spiralin reveals a group by spiralling its members into place, cycle slides each entity to the next’s spot along an arc, and savestate+restore remembers home and snaps back — Manim’s SpiralIn / CyclicReplace / save_state+Restore.

// Three motion moves: `spiralin` reveals a group by spiralling its members into
// place; `cycle` rotates positions along an arc (each slides to the next's spot);
// `savestate` + `restore` remembers where each started and snaps it home. Shown on
// two rows so each reads cleanly. Manim's SpiralIn / CyclicReplace / save_state + Restore.
//
//   manic examples/manim-vs/spiral-cycle-restore.manic
title("Spiral in · cycle · restore");
canvas("16:9");
template("black");

// top row — spiralin reveal
circle(a1, (470, 210), 34); color(a1, cyan);
circle(a2, (570, 210), 34); color(a2, lime);
circle(a3, (670, 210), 34); color(a3, gold);
circle(a4, (770, 210), 34); color(a4, magenta);
tag(a1, top); tag(a2, top); tag(a3, top); tag(a4, top);

// bottom row — cycle, then restore to home
circle(b1, (500, 450), 40); color(b1, cyan);
circle(b2, (640, 450), 40); color(b2, lime);
circle(b3, (780, 450), 40); color(b3, gold);
savestate(b1); savestate(b2); savestate(b3);   // remember each home

text(narr, (640, 630), "Reveal, rearrange, return.", 22);
hidden(narr);

wait(0.4);
show(narr);
wait(1.2);

say(narr, "spiralin — the top group spirals into place, staggered.");
spiralin(top, 1.6);
wait(1.4);

say(narr, "cycle — each bottom dot slides to the next's spot along a 90° arc.");
cycle(b1, b2, b3, 1.3, 90);
wait(1.6);

say(narr, "restore — savestate remembered home, so each snaps back.");
par { restore(b1, 0.8); restore(b2, 0.8); restore(b3, 0.8); }
wait(1.6);

circle-area-proof

The area-of-a-circle LIMIT proof: each stage is a fresh disc CUT into N wedges and ANIMATED — every wedge spins about its tip and slides into a tip-to-tail strip. 4 → 8 → 16 → 32 flattens the humps toward a πr × r rectangle, area πr² throughout. Every strip is for-loop generated from the tessellation formulas (adv = r·sin(π/N), drop = r·cos(π/N)).

// Circle area = πr², the LIMIT proof. Each stage is a fresh disc that is CUT into
// N wedges and then ANIMATED — every wedge spins about its tip and slides into a
// tip-to-tail strip. 4 → 8 → 16 → 32: with more wedges the humps shrink and the
// strip flattens toward a πr × r rectangle. Area πr² all along.
//
// A disc cut into N equal sectors, alternated up/down, tessellates: consecutive
// tips advance r·sin(π/N) and the up/down tip-lines sit r·cos(π/N) apart. Wedge k
// starts at angle k·(360/N); to point up it spins to centre 270°, to point down 90°.
// Everything is a `for` loop over those formulas — no hand-placed geometry.
//
//   manic examples/manim-vs/circle-area-proof.manic
title("Area of a circle = πr²");
canvas("16:9");
template("black");

text(hdr, (640, 66), "Cut a disc into wedges, re-lay them — flatter each time — Manic", 26);

// ---- BUILD every stage's wedges as an assembled disc at (640,300), hidden -----
let sw = 90.0;
for k in 0..4  { sector(w4{k},  (640,300), 150, k*sw, sw); filled(w4{k});  hue(w4{k},  190 + (k - 2*floor(k/2))*140); tag(w4{k},  g4);  }
let sw = 45.0;
for k in 0..8  { sector(w8{k},  (640,300), 150, k*sw, sw); filled(w8{k});  hue(w8{k},  190 + (k - 2*floor(k/2))*140); tag(w8{k},  g8);  }
let sw = 22.5;
for k in 0..16 { sector(w16{k}, (640,300), 150, k*sw, sw); filled(w16{k}); hue(w16{k}, 190 + (k - 2*floor(k/2))*140); tag(w16{k}, g16); }
let sw = 11.25;
for k in 0..32 { sector(w32{k}, (640,300), 150, k*sw, sw); filled(w32{k}); hue(w32{k}, 190 + (k - 2*floor(k/2))*140); tag(w32{k}, g32); }

hidden(g4); hidden(g8); hidden(g16); hidden(g32);
text(cap, (640, 690), "", 24); hidden(cap);
wait(0.3); show(cap);

// ---- ANIMATE each stage: show the cut disc, then spin+slide into the strip ----
// stage 4
let half = 45.0; let ar = pi/4; let adv = 150*sin(ar); let drop = 150*cos(ar); let x0 = 640 - 3*adv/2;
say(cap, "4 wedges — spin them up/down and slide them tip-to-tail.");
show(g4, 0.6); wait(1.0);
par { for k in 0..4  { move(w4{k},  (x0 + k*adv, 440 - (k - 2*floor(k/2))*drop), 1.8, smooth); spin(w4{k},  270 - (k - 2*floor(k/2))*180 - (2*k+1)*half, 1.8); } }
wait(1.6); fade(g4, 0.5);

// stage 8
let half = 22.5; let ar = pi/8; let adv = 150*sin(ar); let drop = 150*cos(ar); let x0 = 640 - 7*adv/2;
say(cap, "8 wedges — flatter already.");
show(g8, 0.6); wait(0.9);
par { for k in 0..8  { move(w8{k},  (x0 + k*adv, 440 - (k - 2*floor(k/2))*drop), 1.7, smooth); spin(w8{k},  270 - (k - 2*floor(k/2))*180 - (2*k+1)*half, 1.7); } }
wait(1.5); fade(g8, 0.5);

// stage 16
let half = 11.25; let ar = pi/16; let adv = 150*sin(ar); let drop = 150*cos(ar); let x0 = 640 - 15*adv/2;
say(cap, "16 wedges — the humps shrink.");
show(g16, 0.6); wait(0.9);
par { for k in 0..16 { move(w16{k}, (x0 + k*adv, 440 - (k - 2*floor(k/2))*drop), 1.7, smooth); spin(w16{k}, 270 - (k - 2*floor(k/2))*180 - (2*k+1)*half, 1.7); } }
wait(1.5); fade(g16, 0.5);

// stage 32 — the limit
let half = 5.625; let ar = pi/32; let adv = 150*sin(ar); let drop = 150*cos(ar); let x0 = 640 - 31*adv/2;
say(cap, "32 wedges — a πr × r rectangle. Area = π r².");
show(g32, 0.6); wait(0.9);
par { for k in 0..32 { move(w32{k}, (x0 + k*adv, 440 - (k - 2*floor(k/2))*drop), 1.7, smooth); spin(w32{k}, 270 - (k - 2*floor(k/2))*180 - (2*k+1)*half, 1.7); } }
wait(2.6);