Transforms & morphing
Each block is the whole file — copy it into x.manic and run manic x.manic (live) or --record out (video).
gradient
Gradient paint, the acceptance demo: one word gradient(id, c1, c2, …, [mode]) covers
a radial well, a three-stop height-colored plot, a spline colored by "curvature", an
RK4 free kick colored by "speed", and an arc-length arrow whose head takes the tip
color. The color is computed, not painted — and every stop stays template-aware.
// Gradient paint — the color is computed, not painted.
// manic examples/gradient.manic
// manic examples/gradient.manic --still 11 --scale 1.5
//
// One word, five truths (stops ≥ 2, evenly spaced; mode optional):
// gradient(path, c1, c2) arc-length along a stroke
// gradient(plot, c1, c2, c3, 270) linear over the bounds — height, any number of stops
// gradient(shape, c1, c2, radial) centre → edge
// gradient(traj, c1, c2, "speed") a trajectory colored by its true local speed
// gradient(path, c1, c2, "curvature") any path colored by how hard it bends
title("Gradient Paint");
canvas(1280, 720);
template("blank");
// --- cast ---
// a radial "energy well" behind everything
circle(well, (640, 400), 260);
filled(well);
gradient(well, panel, void, radial);
opacity(well, 0.85);
// a plot colored by its own height, in three stops: blue low, cyan mid, gold high.
// The vertical linear gradient over the curve's bounds IS its y-value.
axes(ax, (640, 400), 520, 220);
plot(wave, (640, 400), 78, 150, sin, 6.6);
stroke(wave, 4);
gradient(wave, blue, cyan, gold, 270);
untraced(wave);
// a curve colored by its own curvature — the tight apex carries the color
spline(swoop, (160, 640), (400, 260), (640, 160), (880, 260), (1120, 640));
stroke(swoop, 5);
gradient(swoop, dim, magenta, "curvature");
untraced(swoop);
// an RK4 free kick (top-down pitch) colored by its true speed —
// gold off the boot, blue as drag bites (slowest stop first, fastest last)
freekick(kick, (185, 690), 30, 36, 3.5, 5.5, 120);
stroke(kick, 5);
gradient(kick, blue, gold, "speed");
untraced(kick);
hidden(kick.ball);
// an arrow whose head takes the gradient's tip color
arrow(dart, (420, 600), (1040, 600));
stroke(dart, 4);
gradient(dart, dim, lime);
untraced(dart);
// headline + caption
text(head, (640, 100), "the color is computed, not painted");
display(head); color(head, cyan); size(head, 30); hidden(head);
text(cap, (640, 680), ""); color(cap, dim); size(cap, 22);
// --- script ---
show(head, 0.5);
say(cap, "a height-colored plot: blue low, cyan mid, gold high");
draw(wave, 1.8);
wait(0.6);
say(cap, "a curve colored by its own curvature — the bend glows");
draw(swoop, 1.6);
wait(0.6);
say(cap, "a free kick colored by its true speed — gold fast, blue slow");
draw(kick, 1.6);
wait(0.6);
say(cap, "an arc-length stroke; the arrowhead takes its tip color");
draw(dart, 1.2);
wait(1.2);
svg-import
SVG vector import, the acceptance demo: svg() brings a heart (cubic béziers) and its
highlight (a <circle> normalised to a path) in as NATIVE Polyline/Polygon entities —
the #ff2d95 fill kept LITERAL, not re-themed by the template — then show/pulse/recolor
drive the whole drawing through its {id} tag. Geometry, not a texture; the 2D twin of
model3’s OBJ import.
// SVG vector import — bring artwork in as NATIVE path entities (not a texture).
// Each subpath of heart.svg becomes a traceable Polyline / filled Polygon, fitted
// to 320 px wide and centred; the pieces are tagged `heart`, so ONE verb animates
// the whole drawing. Imported colours reproduce faithfully (the #ff2d95 fill is
// kept literal, not re-themed). This is the 2D twin of `model3`'s OBJ import.
title("SVG Vector Import");
canvas("16:9");
template("neon");
svg(heart, (cx, cy), "asset:svg/heart.svg", 320);
hidden(heart);
show(heart, 0.8); // reveal — real geometry, faithful colours
wait(0.4);
pulse(heart, 0.9); // animate the whole drawing with one verb
wait(0.6);
recolor(heart, cyan); // …and recolour it — imported vectors are first-class
wait(1.2);
svg-howto
“Made of Emoji” — a vector spectacle showing the full power of svg(). Thirty rainbow heart
emoji explode from one point into a ring, then snap into a beating HEART placed by the
parametric heart curve — dozens of imported vectors choreographed by pure math. A rapid
SAMPLER then riffles the bundled catalogue (icons · flags · emoji), flags and heroes rain in
with burst confetti, and a rocket launches to the title. Every actor is one svg() line;
the reveal is scale + computed motion + faithful colour, with optional ElevenLabs VO.
// ============================================================================
// svg-howto.manic — "MADE OF EMOJI"
//
// Not a lecture — a spectacle. From one point, a storm of imported emoji
// explodes, swirls into a galaxy, then snaps into a beating heart — dozens of
// vectors choreographed by pure math, exploded again into a flag-lit finale.
// Every actor is an `svg()` import; nothing is a texture; it's all computed.
//
// manic examples/svg-howto.manic
// manic examples/svg-howto.manic --record out --preset reel
// ============================================================================
title("Made of Emoji — a vector spectacle");
canvas("16:9");
template("blank");
voice("elevenlabs"); // promo narration (ELEVENLABS_API_KEY)
// voice("elevenlabs", "alice");
// voice("elevenlabs", "jessica");
// voice("cartesia"); // voice("gtts"); // free fallback
// speak("…") = voice only; say(sub, "…") = on-screen text; both can run together.
// deep-space backdrop
rect(space, (cx, cy), w*2, h*2); filled(space); gradient(space, void, indigo, radial);
text(sub, (cx, h*0.9), ""); size(sub, 30); color(sub, fg); display(sub); glow(sub, 0.4);
// ---- the swarm: 30 heart emoji, rainbow-hued, all born at the centre ----
for i in 0..30 {
svg(sw{i}, (cx, cy), "asset:svg/emoji/heart.svg", 46);
hue(sw{i}, i*12); // rainbow, one hue apiece
tag(sw{i}, swarm); z(sw{i}, 5); hidden(sw{i});
}
// ---- burst engines (gold ignition, cyan confetti) ----
rect(bseed, (cx, cy), 24, 24); hidden(bseed);
particles(spark, bseed, 30, 6, 22, "random"); color(spark, gold); glow(spark, 0.9); hidden(spark);
rect(cseed, (cx, cy), 24, 24); hidden(cseed);
particles(conf, cseed, 30, 6, 22, "random"); color(conf, cyan); glow(conf, 0.9); hidden(conf);
// ---- the finale cast (flags + hero emoji rain in) ----
svg(rk, (cx, h + 120), "asset:svg/emoji/rocket.svg", 120); z(rk, 9); hidden(rk);
svg(hero0, (cx - 500, -80), "asset:svg/emoji/fire.svg", 80); hidden(hero0);
svg(hero1, (cx - 250, -80), "asset:svg/emoji/star.svg", 80); hidden(hero1);
svg(hero2, (cx + 250, -80), "asset:svg/emoji/party.svg", 80); hidden(hero2);
svg(hero3, (cx + 500, -80), "asset:svg/emoji/trophy.svg", 80); hidden(hero3);
svg(flag0, (cx - 420, -80), "asset:svg/flags/in.svg", 92); hidden(flag0);
svg(flag1, (cx - 140, -80), "asset:svg/flags/br.svg", 92); hidden(flag1);
svg(flag2, (cx + 140, -80), "asset:svg/flags/jp.svg", 92); hidden(flag2);
svg(flag3, (cx + 420, -80), "asset:svg/flags/de.svg", 92); hidden(flag3);
// ---- the sampler: a stack of diverse shapes to riffle through ----
svg(smp0, (cx, cy - 40), "asset:svg/emoji/rocket.svg", 220); hidden(smp0);
svg(smp1, (cx, cy - 40), "asset:svg/emoji/party.svg", 220); hidden(smp1);
svg(smp2, (cx, cy - 40), "asset:svg/emoji/brain.svg", 220); hidden(smp2);
svg(smp3, (cx, cy - 40), "asset:svg/flags/jp.svg", 240); hidden(smp3);
svg(smp4, (cx, cy - 40), "asset:svg/emoji/trophy.svg", 220); hidden(smp4);
svg(smp5, (cx, cy - 40), "asset:svg/lucide/cpu.svg", 200); hue(smp5, 190); hidden(smp5);
svg(smp6, (cx, cy - 40), "asset:svg/flags/br.svg", 240); hidden(smp6);
svg(smp7, (cx, cy - 40), "asset:svg/emoji/fire.svg", 220); hidden(smp7);
svg(smp8, (cx, cy - 40), "asset:svg/heroicons/bolt.svg", 200); hue(smp8, 48); hidden(smp8);
svg(smp9, (cx, cy - 40), "asset:svg/flags/in.svg", 240); hidden(smp9);
svg(smp10, (cx, cy - 40), "asset:svg/emoji/star.svg", 220); hidden(smp10);
svg(smp11, (cx, cy - 40), "asset:svg/lucide/globe.svg", 200); hue(smp11, 130); hidden(smp11);
svg(smp12, (cx, cy - 40), "asset:svg/emoji/earth.svg", 220); hidden(smp12);
svg(smp13, (cx, cy - 40), "asset:svg/flags/de.svg", 240); hidden(smp13);
svg(smp14, (cx, cy - 40), "asset:svg/heroicons/beaker.svg", 200); hue(smp14, 300); hidden(smp14);
svg(smp15, (cx, cy - 40), "asset:svg/emoji/heart.svg", 220); hidden(smp15);
text(mark, (cx, cy - 20), "MANIC"); size(mark, 110); color(mark, gold); bold(mark); display(mark); glow(mark, 1.2); hidden(mark);
text(tagl, (cx, cy + 78), "6,404 shapes · one file · all computed");
size(tagl, 26); color(tagl, cyan); display(tagl); hidden(tagl);
// ============================================================================
// THE SPECTACLE (fast, punchy)
// ============================================================================
// 1 — SINGULARITY: everything waits in one blinding point.
seq {
zoom(1.35, 0.01);
show(sw0, 0.3);
par { for i in 0..30 { show(sw{i}, 0.25); } } // a tight rainbow knot at centre
pulse(sw0);
say(sub, "One line…", 0.25);
speak("This… is one line of Manic.");
wait(0.2);
}
// 2 — BIG BANG: the storm explodes outward into a ring.
seq {
say(sub, "…one universe.", 0.25);
speak("From it, a whole universe of vectors explodes into being.");
show(spark, 0.05); burst(spark, 0.7);
par {
for i in 0..30 {
move(sw{i}, (cx + 300*cos(i*12*pi/180), cy + 300*sin(i*12*pi/180)), 0.7, out);
}
zoom(1.0, 0.7, smooth);
}
wait(0.15);
}
// 3 — GALAXY: the ring breathes (turn removed to test formation).
seq {
say(sub, "computed — never drawn.", 0.3);
speak("Import any SVG — an icon, a flag, an emoji — as living geometry. Not a picture. Real shapes you can move and colour.");
par { for i in 0..30 { pulse(sw{i}); } }
wait(0.2);
}
// 4 — THE HEART: dozens of vectors snap into a single shape.
seq {
say(sub, "shape them at will.", 0.3);
speak("Watch thirty of them snap into a single form — placed by pure math.");
par {
for i in 0..30 {
move(sw{i},
(cx + 208*sin(i*pi/15)*sin(i*pi/15)*sin(i*pi/15),
cy - (169*cos(i*pi/15) - 65*cos(2*i*pi/15) - 26*cos(3*i*pi/15) - 13*cos(4*i*pi/15)) + 40),
1.1, smooth);
}
}
par { for i in 0..30 { pulse(sw{i}); } } // the heart beats
wait(0.4);
}
// 4.5 — THE LIBRARY: riffle the catalogue so they see the full range.
seq {
say(sub, "and not just hearts — 6,404 shapes, one line each.", 0.3);
speak("And it's not just hearts. Six thousand four hundred shapes ship inside — every icon, every flag, every emoji — each one line away.");
par { for i in 0..30 { fade(sw{i}, 0.4); } } // clear the heart
stagger(0.2) {
for i in 0..16 { seq { show(smp{i}, 0.14); fade(smp{i}, 0.3); } }
}
wait(0.2);
}
// 5 — CELEBRATION: flags and heroes rain in; confetti pops.
seq {
say(sub, "every shape. every colour. one file.", 0.35);
speak("Faithful colours. Real vectors. Recolour them, move them, explode them — all from one text file.");
show(conf, 0.05); burst(conf, 0.9);
stagger(0.06) {
move(flag0, (cx - 420, cy - 250), 0.7, overshoot); show(flag0, 0.1);
move(hero0, (cx - 500, cy + 250), 0.7, overshoot); show(hero0, 0.1);
move(flag1, (cx - 140, cy - 250), 0.7, overshoot); show(flag1, 0.1);
move(hero1, (cx - 250, cy + 250), 0.7, overshoot); show(hero1, 0.1);
move(flag2, (cx + 140, cy - 250), 0.7, overshoot); show(flag2, 0.1);
move(hero2, (cx + 250, cy + 250), 0.7, overshoot); show(hero2, 0.1);
move(flag3, (cx + 420, cy - 250), 0.7, overshoot); show(flag3, 0.1);
move(hero3, (cx + 500, cy + 250), 0.7, overshoot); show(hero3, 0.1);
}
wait(0.4);
}
// 6 — FINALE: it all bursts apart; a rocket tears through to the title.
seq {
say(sub, "", 0.1);
show(spark, 0.05); burst(spark, 0.8);
par {
fade(flag0, 0.5); fade(flag1, 0.5); fade(flag2, 0.5); fade(flag3, 0.5);
fade(hero0, 0.5); fade(hero1, 0.5); fade(hero2, 0.5); fade(hero3, 0.5);
}
show(rk, 0.2);
move(rk, (cx, cy + 40), 0.7, out);
par { flash(rk, cyan); pulse(rk); }
move(rk, (cx, -160), 0.6, in); // it launches off the top
par { show(mark, 0.5); show(tagl, 0.5); }
speak("This is Manic — turn plain text into vector spectacle. Try it free at eight g wifi dot org slash manic.");
flash(mark, gold);
wait(1.4);
}
clip-window
T1 rectangular clip: clip(id, region) scissors a big imported vector to another entity’s
bounds, so a hollow spotlight window sweeps across a flag and reveals it piece by piece. The
scissor is resolved every frame — move the window and the reveal tracks it. Reveal-through-a-
window · crop-to-panel · region wipe · inset viewport, in one word.
// clip-window.manic — T1 rectangular clip: reveal-through-a-window / crop-to-panel.
//
// `clip(id, region)` scissors an entity (or a whole tagged group) to another
// entity's rectangular bounds. The scissor is resolved every frame, so the window
// FOLLOWS the region — move the window and the reveal tracks it. Here a spotlight
// window sweeps across a big imported vector, revealing it piece by piece.
//
// manic examples/clip-window.manic
title("Clip — reveal through a window");
canvas("16:9");
template("blank");
// a big imported vector that overflows what one window can show
svg(art, (cx, cy), "asset:svg/flags/br.svg", 940);
// the spotlight window — hollow frame so the clipped vector shows through
// (declared BEFORE the clip that references it)
rect(win, (cx - 380, cy), 320, 300); outlined(win); outline(win, fg); stroke(win, 5); glow(win, 0.7);
// clip the whole vector to the window — nothing shows outside it
clip(art, win);
text(cap, (cx, h*0.9), "clip(id, window) — the scissor follows the window");
size(cap, 26); color(cap, dim); display(cap);
// sweep the window across → the flag is revealed only where the window is
seq {
wait(0.4);
move(win, (cx + 380, cy), 2.4, smooth);
wait(0.3);
move(win, (cx, cy), 1.0, smooth);
pulse(win);
wait(0.6);
}
mask-lens
T2 arbitrary-shape mask: mask(id, region) composites a vector through another entity’s
SILHOUETTE — any shape masks any content (render-target multiply). A circular lens glides
across a flag like a magnifier; swap the circle for a star or an imported outline and the
reveal takes that shape. The general cousin of clip (rectangle-only). Lens · porthole ·
spotlight · reveal-through-any-shape, and the mask follows the region every frame.
// mask-lens.manic — T2 arbitrary-shape mask: reveal-through-any-shape.
//
// `mask(id, region)` composites `id` through another entity's SILHOUETTE — any
// shape (circle, polygon, star, an imported svg outline) masks any content. The
// general cousin of `clip` (which is rectangle-only). The mask follows `region`
// every frame, so a circular lens glides across a big vector like a magnifier.
//
// manic examples/mask-lens.manic
title("Mask — reveal through any shape");
canvas("16:9");
template("blank");
// a big imported vector to peer into
svg(art, (cx, cy), "asset:svg/flags/br.svg", 940);
// a circular lens — its shape is the mask (declared BEFORE the mask call)
circle(lens, (cx - 340, cy), 150); outlined(lens); outline(lens, fg); stroke(lens, 5); glow(lens, 0.8);
// mask the whole vector to the lens — art shows only inside the circle
mask(art, lens);
text(cap, (cx, h*0.9), "mask(id, region) — any shape masks any content");
size(cap, 26); color(cap, dim); display(cap);
// glide the lens across → the flag is revealed only inside the moving circle
seq {
wait(0.4);
move(lens, (cx + 340, cy), 2.4, smooth);
wait(0.3);
move(lens, (cx, cy), 1.0, smooth);
pulse(lens);
wait(0.6);
}
motion-graphics-v2
The generic Motion Graphics V2 acceptance scene: one persistent marker carries an attached
label along a path, becomes a declared visual blueprint, releases the label, gathers the
same particles into a ring, and turns the whole arrangement around one shared pivot. Uses
attach, become, and turn with no renderer flags or subject-specific vocabulary.
// motion-graphics-v2.manic — relationships, identity, and a deliberate settle
title("Motion Graphics V2 — Follow, Become, Turn");
canvas("9:16");
template("mono");
watermark(manicMark, (150, 132), "Made With Manic");
creator(me, "@anish2good name=Manic_Motion tagline=Describe_the_relationship yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=cyan secondary=magenta footer=social cta=Create_yours safe=reels");
socials(me);
text(kicker, (540, 155), "MOTION GRAPHICS V2");
size(kicker, 23); color(kicker, dim); bold(kicker);
text(headline, (540, 228), "Move the idea, not every coordinate");
size(headline, 40); bold(headline);
text(subhead, (540, 292), "attach · become · turn");
size(subhead, 25); color(subhead, dim);
rect(stage, (540, 865), 900, 1020);
color(stage, panel); outline(stage, dim); opacity(stage, 0.78);
// One authored route and one persistent object.
plot(route, (205, 1040), 172, 190, "0.55*sin(1.2*x)+0.18*x", (0, 4.25));
color(route, cyan); stroke(route, 6); untraced(route);
dot(marker, (205, 1040), 12);
color(marker, gold); glow(marker, 1.6);
text(markerLabel, (205, 994), "one persistent object");
size(markerLabel, 24); color(markerLabel, fg); hidden(markerLabel);
// A hidden target is a visual blueprint; it never appears as a second object.
circle(nodeBlueprint, (935, 885), 56);
color(nodeBlueprint, magenta); outlined(nodeBlueprint); stroke(nodeBlueprint, 7);
glow(nodeBlueprint, 1.2); hidden(nodeBlueprint);
// Supporting particles begin offstage, then arrive as an ordered ring.
rect(seedBox, (540, 610), 260, 180); hidden(seedBox);
particles(sparks, seedBox, 14, 7, 42, "random");
color(sparks, cyan); hidden(sparks);
circle(finalOrbit, (935, 885), 138); hidden(finalOrbit);
text(explain, (540, 1325), "The label follows because the relationship is authored.");
size(explain, 26); color(explain, dim); wrap(explain, 800); hidden(explain);
step("attach-and-travel") {
seq {
attach(markerLabel, marker, (0,-46));
par {
show(markerLabel, 0.35);
show(explain, 0.40);
draw(route, 2.8, smooth);
travel(marker, route, 2.8, smooth);
}
wait(0.45);
}
}
step("become-the-next-idea") {
seq {
par {
become(marker, nodeBlueprint, 1.05, smooth);
say(markerLabel, "same id · new visual state", 0.45);
say(explain, "become preserves identity and settles on the exact blueprint.", 0.45);
}
wait(0.50);
}
}
step("release-arrange-turn") {
seq {
attach(markerLabel, none);
par {
fade(markerLabel, 0.35);
fade(route, 0.45);
show(sparks, 0.30);
arrange(sparks, finalOrbit, "ring", 1.15, smooth);
say(explain, "Release the label. Arrange a system. Turn it around one pivot.", 0.45);
}
turn(sparks, marker, 24, 0.65, out);
wait(1.40);
}
}
motion-graphics-v2-story
The advanced composition example: one question travels through a field of facts, its WHY?
label follows, notation rewrites into a pattern, the question becomes a model, and the same
facts arrange and turn as one knowledge system. Combines attach, become, and turn with
to, travel, flow, spin, arrange, wander, rewrite, seq, par, and stagger.
// motion-graphics-v2-story.manic — one idea, one continuous motion story
title("The Journey of an Idea — Motion Graphics V2 Story");
canvas("9:16");
template("mono");
watermark(manicMark, (170, 178), "Made With Manic");
creator(me, "@anish2good name=Manic_Motion tagline=Move_the_idea yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=cyan secondary=magenta footer=social cta=Create_yours safe=reels");
socials(me);
// ---------------------------------------------------------------------------
// ONE PERSISTENT STAGE
// The scene never cuts away. Each act reuses what the previous act established.
// ---------------------------------------------------------------------------
text(kicker, (540, 184), "A MOTION GRAPHICS STORY");
size(kicker, 22); color(kicker, dim); bold(kicker); hidden(kicker);
text(headline, (540, 248), "How an idea learns to move");
size(headline, 42); bold(headline); hidden(headline);
text(chapter, (540, 316), "ACT 1 · FOLLOW ATTENTION");
size(chapter, 24); color(chapter, cyan); bold(chapter); hidden(chapter);
rect(stage, (540, 850), 900, 990);
color(stage, panel); outline(stage, dim); opacity(stage, 0.76);
text(narration, (540, 1400), "A question appears before the answer does.");
size(narration, 28); color(narration, fg); wrap(narration, 820); hidden(narration);
text(principle, (540, 1495), "Motion should guide the eye, preserve meaning, then settle.");
size(principle, 22); color(principle, dim); wrap(principle, 820); hidden(principle);
// ---------------------------------------------------------------------------
// ACT 1 — CURIOSITY TRAVELS; ITS CONTEXT FOLLOWS
// ---------------------------------------------------------------------------
spline(questionPath, (175, 1080), (290, 980), (385, 1060), (490, 900), (540, 790));
color(questionPath, cyan); stroke(questionPath, 5); dashed(questionPath, 18, 15);
glow(questionPath, 0.8); untraced(questionPath);
dot(idea, (175, 1080), 12);
color(idea, gold); glow(idea, 1.7); hidden(idea);
text(focusLabel, (175, 1024), "WHY?");
size(focusLabel, 29); color(focusLabel, gold); bold(focusLabel); hidden(focusLabel);
// Facts begin as visual noise. They persist through every later arrangement.
rect(noiseField, (540, 820), 690, 470); hidden(noiseField);
particles(facts, noiseField, 16, 7, 29, "random");
color(facts, cyan); glow(facts, 0.9); hidden(facts);
for i in 0..16 {
tag(facts.p{i}, knowledgeSystem);
}
// ---------------------------------------------------------------------------
// ACT 2 — THE SAME QUESTION BECOMES A MODEL
// ---------------------------------------------------------------------------
equation(work, (540, 1195), `1+2+\cdots+n=?`, 43);
color(work, fg); hidden(work);
// Hidden entities are visual blueprints. `idea` adopts this exact final state.
rect(modelBlueprint, (540, 790), 116, 116);
outlined(modelBlueprint); outline(modelBlueprint, magenta); stroke(modelBlueprint, 7);
rot(modelBlueprint, 45); glow(modelBlueprint, 1.5); hidden(modelBlueprint);
circle(knowledgeOrbit, (540, 790), 250); hidden(knowledgeOrbit);
text(observe, (540, 492), "OBSERVE");
text(connect, (842, 790), "CONNECT");
text(testIdea, (540, 1088), "TEST");
text(explain, (238, 790), "EXPLAIN");
size(observe, 20); size(connect, 20); size(testIdea, 20); size(explain, 20);
color(observe, dim); color(connect, dim); color(testIdea, dim); color(explain, dim);
bold(observe); bold(connect); bold(testIdea); bold(explain);
hidden(observe); hidden(connect); hidden(testIdea); hidden(explain);
tag(observe, knowledgeSystem); tag(connect, knowledgeSystem);
tag(testIdea, knowledgeSystem); tag(explain, knowledgeSystem);
// ---------------------------------------------------------------------------
// ACT 3 — THE MODEL BECOMES A COORDINATED SYSTEM
// ---------------------------------------------------------------------------
text(systemLabel, (540, 648), "ONE IDEA · MANY RELATIONSHIPS");
size(systemLabel, 22); color(systemLabel, magenta); bold(systemLabel); hidden(systemLabel);
text(finalLine, (540, 1320), "Don't animate layers. Animate the relationship.");
size(finalLine, 30); color(finalLine, fg); bold(finalLine); wrap(finalLine, 820); hidden(finalLine);
// ---------------------------------------------------------------------------
// TIMELINE — seq tells the story; par and stagger control the viewing rhythm.
// ---------------------------------------------------------------------------
step("a-question-pulls-attention") {
seq {
par {
show(kicker, 0.35);
show(headline, 0.55);
show(chapter, 0.40);
show(narration, 0.45);
show(principle, 0.45);
show(idea, 0.30);
}
// Stagger turns a cloud into a readable arrival instead of one visual shock.
stagger(0.035) {
for i in 0..16 {
show(facts.p{i}, 0.22);
}
}
attach(focusLabel, idea, (0,-56));
par {
show(focusLabel, 0.30);
draw(questionPath, 2.65, smooth);
travel(idea, questionPath, 2.65, smooth);
flow(questionPath, 1.45);
wander(facts, 2.65);
}
par {
to(idea, scale, 1.55, 0.45, out);
say(narration, "Attention follows the question while the background stays alive.", 0.45);
}
wait(0.55);
}
}
step("the-question-becomes-a-model") {
seq {
attach(focusLabel, none);
par {
fade(focusLabel, 0.30);
fade(questionPath, 0.45);
say(chapter, "ACT 2 · PRESERVE THE IDEA", 0.35);
say(narration, "The question changes form, but it never loses its identity.", 0.45);
show(work, 0.40);
}
// Existing notation moves only where the mathematical meaning changes.
rewrite(work, `1+2+\cdots+n=\frac{n(n+1)}{2}`, 1.00, smooth);
par {
become(idea, modelBlueprint, 1.05, smooth);
arrange(facts, knowledgeOrbit, "ring", 1.20, smooth);
say(principle, "become keeps one id · arrange keeps every particle", 0.45);
}
// spin is local: the new model turns around its own centre.
spin(idea, 90, 0.65, out);
stagger(0.10) {
show(observe, 0.30);
show(connect, 0.30);
show(testIdea, 0.30);
show(explain, 0.30);
}
wait(0.55);
}
}
step("relationships-move-as-one") {
seq {
attach(systemLabel, idea, (0,-142));
par {
show(systemLabel, 0.35);
say(chapter, "ACT 3 · MOVE THE SYSTEM", 0.35);
say(narration, "Now every part can move together without losing the structure.", 0.45);
show(finalLine, 0.45);
}
// turn is relational: particles and labels share one pivot and one motion.
par {
turn(knowledgeSystem, idea, 45, 1.20, smooth);
spin(idea, -135, 1.20, smooth);
to(facts, color, lime, 1.20, smooth);
rewrite(work, `\text{question}\;\longrightarrow\;\text{model}\;\longrightarrow\;\text{insight}`, 1.20, smooth);
}
par {
pulse(idea, 0.70);
say(principle, "FOLLOW · TRANSFORM · COORDINATE · SETTLE", 0.45);
}
wait(1.80);
}
}
reactive-math-journey
A playful vertical journey from 1+1 in Class 1 through fractions, algebra,
geometry, calculus, linear algebra, probability and Fourier analysis to a PhD-level
functional integral — then back to the curiosity that started it all.
// ============================================================================
// reactive-math-journey.manic — from Class 1 counting to PhD curiosity
// ----------------------------------------------------------------------------
// One equation id grows with the learner. `rewrite` keeps the journey continuous
// while a progress marker climbs through school, university and research.
// The ending returns to 1+1: advanced mathematics keeps the original curiosity.
// ============================================================================
title("From 1 + 1 to a PhD in Mathematics");
canvas("9:16");
template("shorts");
watermark(manicMark, (w*0.895-100, h*0.075+24), "Made With Manic");
creator(me, "@anish2good name=Math_Journey tagline=Stay_curious yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=cyan secondary=magenta footer=social cta=Keep_learning safe=reels");
socials(me);
text(kicker, (540, 135), "THE JOURNEY OF MATHEMATICS");
size(kicker, 23); color(kicker, dim); bold(kicker); hidden(kicker);
text(headline, (540, 225), "From 1 + 1 to a PhD");
size(headline, 47); color(headline, fg); bold(headline); hidden(headline);
text(subhead, (540, 292), "Same curiosity. Bigger questions.");
size(subhead, 25); color(subhead, cyan); bold(subhead); hidden(subhead);
// The learning stage leaves a slim left lane for the climbing progress marker.
rect(stage, (610, 790), 820, 760);
color(stage, panel); outline(stage, dim); opacity(stage, 0.78);
line(rail, (135, 1135), (135, 535));
color(rail, cyan); stroke(rail, 3); dashed(rail, 13, 10); opacity(rail, 0.35);
untraced(rail);
for i in 0..13 {
dot(milestone{i}, (135, 1135 - i*50), 5);
color(milestone{i}, dim); opacity(milestone{i}, 0.55);
}
dot(learner, (135, 1135), 12);
color(learner, cyan); glow(learner, 2.2); hidden(learner);
text(level, (610, 495), "CLASS 1 · COUNTING");
size(level, 25); color(level, cyan); bold(level); hidden(level);
equation(work, (610, 775), `1+1=2`, 58);
hidden(work);
text(note, (610, 1035), "Two apples. One idea. A lifetime of questions begins.");
size(note, 27); color(note, dim); wrap(note, 700); hidden(note);
text(altitude, (540, 1265), "SCHOOL → UNIVERSITY → RESEARCH");
size(altitude, 22); color(altitude, dim); bold(altitude); hidden(altitude);
// ---------------------------------------------------------------------------
// TIMELINE
// ---------------------------------------------------------------------------
par {
show(kicker, 0.40); show(headline, 0.55); show(subhead, 0.55);
show(level, 0.45); show(note, 0.45); show(altitude, 0.45);
draw(rail, 0.90); show(learner, 0.45);
}
show(work, 0.55);
wait(0.85);
// Class 2 — repeated addition discovers multiplication.
par {
say(level, "CLASS 2 · MULTIPLICATION", 0.35);
say(note, "Addition gets impatient and invents a shortcut.", 0.35);
move(learner, (135, 1085), 0.45, smooth);
}
rewrite(work, `3+3+3+3=12`, 0.80, smooth);
wait(0.35);
rewrite(work, `4\times3=\textcolor{lime}{12}`, 0.80, smooth);
wait(0.65);
// Class 5 — pieces become numbers too.
par {
say(level, "CLASS 5 · FRACTIONS", 0.35);
say(note, "Now even pieces of a whole can be added exactly.", 0.35);
move(learner, (135, 1035), 0.45, smooth);
}
rewrite(work, `\frac{1}{2}+\frac{1}{3}=\textcolor{lime}{\frac{5}{6}}`, 0.90, smooth);
wait(0.70);
// Class 7 — the mystery number gets a name.
par {
say(level, "CLASS 7 · ALGEBRA", 0.35);
say(note, "A blank box becomes x — and arithmetic starts telling stories.", 0.35);
move(learner, (135, 985), 0.45, smooth);
}
rewrite(work, `x+3=7`, 0.80, smooth);
wait(0.30);
rewrite(work, `x=\textcolor{lime}{4}`, 0.75, smooth);
wait(0.65);
// Class 8 — shapes reveal equations hidden inside them.
par {
say(level, "CLASS 8 · GEOMETRY", 0.35);
say(note, "A right triangle quietly connects three squares.", 0.35);
move(learner, (135, 935), 0.45, smooth);
}
rewrite(work, `a^2+b^2=\textcolor{cyan}{c^2}`, 0.85, smooth);
wait(0.70);
// Class 10 — one formula solves every quadratic.
par {
say(level, "CLASS 10 · QUADRATICS", 0.35);
say(note, "The unknown now has two possible futures.", 0.35);
move(learner, (135, 885), 0.45, smooth);
}
rewrite(work, `ax^2+bx+c=0`, 0.85, smooth);
wait(0.30);
rewrite(work, `x=\frac{-b\pm\sqrt{b^2-4ac}}{2a}`, 1.00, smooth);
wait(0.75);
// Class 11 — circles turn into identities.
par {
say(level, "CLASS 11 · TRIGONOMETRY", 0.35);
say(note, "Sine and cosine travel differently, but always return to one.", 0.35);
move(learner, (135, 835), 0.45, smooth);
}
rewrite(work, `\sin^2\theta+\cos^2\theta=\textcolor{lime}{1}`, 0.90, smooth);
wait(0.70);
// Class 12 — mathematics learns to describe change itself.
par {
say(level, "CLASS 12 · CALCULUS", 0.35);
say(note, "Not just where things are — how fast they are changing.", 0.35);
move(learner, (135, 785), 0.45, smooth);
}
rewrite(work, `\frac{d}{dx}x^n=\textcolor{cyan}{nx^{n-1}}`, 0.95, smooth);
wait(0.75);
// University — numbers become transformations and uncertain beliefs.
par {
say(level, "UNIVERSITY · LINEAR ALGEBRA", 0.35);
say(note, "Some directions do not turn — they only stretch.", 0.35);
move(learner, (135, 735), 0.45, smooth);
}
rewrite(work, `A\vec v=\textcolor{magenta}{\lambda}\vec v`, 0.90, smooth);
wait(0.70);
par {
say(level, "UNIVERSITY · PROBABILITY", 0.35);
say(note, "Evidence arrives, and a belief learns how to update.", 0.35);
move(learner, (135, 685), 0.45, smooth);
}
rewrite(work, `P(A\mid B)=\frac{P(B\mid A)P(A)}{P(B)}`, 0.95, smooth);
wait(0.75);
// Graduate study — equations evolve, then decompose into waves.
par {
say(level, "GRADUATE · DIFFERENTIAL EQUATIONS", 0.35);
say(note, "A tiny law of change predicts an entire future.", 0.35);
move(learner, (135, 635), 0.45, smooth);
}
rewrite(work, `\frac{dy}{dt}=ky`, 0.85, smooth);
wait(0.30);
rewrite(work, `y(t)=y_0e^{kt}`, 0.85, smooth);
wait(0.70);
par {
say(level, "GRADUATE · FOURIER ANALYSIS", 0.35);
say(note, "A complicated signal turns out to be a choir of simple waves.", 0.35);
move(learner, (135, 585), 0.45, smooth);
}
rewrite(work, `f(x)=\sum_{n=-\infty}^{\infty}c_ne^{inx}`, 0.95, smooth);
wait(0.75);
// PhD — the symbols are advanced; the habit is still “what if?”
par {
say(level, "PhD · THE EDGE OF WHAT WE KNOW", 0.40);
say(note, "The integral now ranges over every possible field configuration.", 0.40);
move(learner, (135, 535), 0.55, overshoot);
}
rewrite(work, `\mathcal{Z}=\int\mathcal{D}\phi\,e^{-S[\phi]}`, 1.05, smooth);
wait(1.00);
// A loopable ending: the notation grew, but the first question never left.
say(level, "THE REAL DEGREE · STAYING CURIOUS", 0.40);
say(note, "After all that mathematics, the best researchers still ask: why?", 0.40);
rewrite(work, `1+1=\textcolor{lime}{2}`, 1.00, smooth);
pulse(learner, 0.65);
wait(1.80);
reactive-math-notation
One Reels-ready stage exercises structured LaTeX across thirteen notation worlds: algebra, calculus, limits, trigonometry, logic, sums/products, physics, chemistry, biology, probability, matrices/vectors, mixed prose/math, and creator notation.
// ============================================================================
// reactive-math-notation.manic — one Reels-ready stage, thirteen notation worlds
// ----------------------------------------------------------------------------
// `rewrite` is deliberately domain-neutral. The same persistent equation moves
// through algebra, calculus, logic, physics, chemistry, biology, probability
// and linear algebra;
// only changed RaTeX parts leave or enter. Mixed prose + inline math uses normal
// `text`, and the last scene proves creator-defined notation needs no new verb.
// ============================================================================
title("Reactive Mathematics — One Language, Every Notation");
canvas("9:16");
template("shorts");
watermark(manicMark, (w*0.895-100, h*0.075+24), "Made With Manic");
creator(me, "@anish2good name=Notation_Lab tagline=Every_symbol_can_move yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=cyan secondary=magenta footer=social cta=Create_your_own safe=reels");
socials(me);
text(kicker, (540, 145), "ONE EQUATION · THIRTEEN SCIENTIFIC WORLDS");
size(kicker, 23); color(kicker, dim); bold(kicker); hidden(kicker);
text(headline, (540, 235), "If LaTeX can say it, Manic can move it");
size(headline, 39); color(headline, fg); bold(headline); hidden(headline);
rect(stage, (540, 755), 940, 760);
color(stage, panel); outline(stage, dim); opacity(stage, 0.72); hidden(stage);
text(topic, (540, 445), "01 · ALGEBRAIC REARRANGEMENT");
size(topic, 23); color(topic, cyan); bold(topic); hidden(topic);
equation(work, (540, 730), `2(x+3)=14`, 52);
hidden(work);
text(note, (540, 1040), "Keep the structure. Move only the mathematical change.");
size(note, 25); color(note, dim); wrap(note, 800); hidden(note);
// A normal text entity may freely mix prose and several inline formulas.
text(mixed, (540, 735), `Energy $E=mc^2$ uses mass $m$ and light speed $c$.`);
size(mixed, 31); color(mixed, fg); wrap(mixed, 780); hidden(mixed);
text(scope, (540, 1245), "RaTeX accuracy · local motion · one stable layout");
size(scope, 22); color(scope, dim); bold(scope); hidden(scope);
// A subtle dashed construction line also demonstrates that `dashed` belongs
// to Manic itself — plots and all other path-like entities can use it.
line(rule, (215, 1155), (865, 1155));
color(rule, cyan); stroke(rule, 2); dashed(rule, 12, 9); opacity(rule, 0.35);
untraced(rule);
// ---------------------------------------------------------------------------
// TIMELINE — every chapter reuses `work`; nothing is manually repositioned.
// ---------------------------------------------------------------------------
par {
show(kicker, 0.45); show(headline, 0.55); show(stage, 0.50);
show(topic, 0.45); show(note, 0.45); show(scope, 0.45); draw(rule, 0.85);
}
show(work, 0.55);
wait(0.70);
rewrite(work, `2x=8\quad\Rightarrow\quad \textcolor{lime}{x=4}`, 0.85, smooth);
wait(0.75);
say(topic, "02 · INTEGRALS & DERIVATIVES", 0.35);
say(note, "The Fundamental Theorem becomes a change of state, not a scene cut.", 0.35);
rewrite(work, `F(x)=\int_0^x t^2\,dt`, 0.80, smooth);
wait(0.45);
rewrite(work, `F'(x)=\frac{d}{dx}\int_0^x t^2\,dt=\textcolor{cyan}{x^2}`, 0.95, smooth);
wait(0.70);
say(topic, "03 · FRACTIONS, ROOTS, POWERS & LIMITS", 0.35);
say(note, "Nested notation is still one screen-aware equation.", 0.35);
rewrite(work, `x^2+\sqrt{x}+\frac{1}{x}`, 0.80, smooth);
wait(0.40);
rewrite(work, `\lim_{x\to0}\frac{\sqrt{1+x}-1}{x}=\frac{1}{2}`, 0.95, smooth);
wait(0.70);
say(topic, "04 · TRIGONOMETRIC IDENTITIES", 0.35);
say(note, "The unchanged terms stay; only the conclusion arrives.", 0.35);
rewrite(work, `\sin^2\theta+\cos^2\theta`, 0.80, smooth);
wait(0.40);
rewrite(work, `\sin^2\theta+\cos^2\theta=\textcolor{lime}{1}`, 0.85, smooth);
wait(0.70);
say(topic, "05 · SET NOTATION & LOGIC", 0.35);
say(note, "Membership, intersections and propositions share the same engine.", 0.35);
rewrite(work, `x\in A\cap B`, 0.80, smooth);
wait(0.40);
rewrite(work, `(x\in A)\land(x\in B)`, 0.85, smooth);
wait(0.70);
say(topic, "06 · SUMMATIONS & PRODUCTS", 0.35);
say(note, "Large operators retain their limits and baseline alignment.", 0.35);
rewrite(work, `\sum_{k=1}^{n}k`, 0.80, smooth);
wait(0.40);
rewrite(work, `\prod_{k=1}^{n}k=\textcolor{magenta}{n!}`, 0.85, smooth);
wait(0.70);
say(topic, "07 · PHYSICS FORMULAS & UNITS", 0.35);
say(note, "Symbols and dimensional units can live in one exact expression.", 0.35);
rewrite(work, `F=ma`, 0.80, smooth);
wait(0.40);
rewrite(work, `[F]=\mathrm{kg}\cdot\mathrm{m}\cdot\mathrm{s}^{-2}`, 0.90, smooth);
wait(0.70);
say(topic, "08 · CHEMISTRY · BALANCED REACTIONS", 0.35);
say(note, "Keep every molecule; introduce only the coefficients that balance atoms.", 0.35);
rewrite(work, `H_2+O_2\rightarrow H_2O`, 0.80, smooth);
wait(0.40);
rewrite(work, `\textcolor{cyan}{2}H_2+O_2\rightarrow\textcolor{cyan}{2}H_2O`, 0.90, smooth);
wait(0.70);
say(topic, "09 · BIOLOGY · HARDY–WEINBERG", 0.35);
say(note, "A population model grows from allele frequencies into genotype frequencies.", 0.35);
rewrite(work, `p+q=1`, 0.80, smooth);
wait(0.40);
rewrite(work, `p^2+2pq+q^2=\textcolor{lime}{1}`, 0.90, smooth);
wait(0.70);
say(topic, "10 · PROBABILITY EXPRESSIONS", 0.35);
say(note, "Conditioning expands into Bayes' rule without replacing the stage.", 0.35);
rewrite(work, `P(A\mid B)`, 0.80, smooth);
wait(0.40);
rewrite(work, `P(A\mid B)=\frac{P(B\mid A)P(A)}{P(B)}`, 0.95, smooth);
wait(0.70);
say(topic, "11 · MATRICES & VECTORS", 0.35);
say(note, "Rows, columns, brackets and vector marks remain genuine LaTeX.", 0.35);
rewrite(work, `\vec v=\begin{bmatrix}1\\2\end{bmatrix}`, 0.85, smooth);
wait(0.40);
rewrite(work, `A\vec v=\begin{bmatrix}a&b\\c&d\end{bmatrix}\begin{bmatrix}1\\2\end{bmatrix}`, 1.00, smooth);
wait(0.75);
say(topic, "12 · TEXT MIXED WITH MATHEMATICS", 0.35);
say(note, "Ordinary prose can carry multiple inline formulas naturally.", 0.35);
fade(work, 0.35);
show(mixed, 0.55);
wait(1.25);
fade(mixed, 0.35);
say(topic, "13 · YOUR OWN NOTATION", 0.35);
say(note, "Define the meaning in your story; Manic preserves the supported notation.", 0.35);
rewrite(work, `\mathcal{R}_{\star}(x)\equiv x^2+1`, 0.90, smooth);
wait(0.45);
rewrite(work, `\mathcal{R}_{\star}(2)=\textcolor{lime}{5}`, 0.85, smooth);
wait(1.20);
say(topic, "ONE REACTIVE LANGUAGE", 0.40);
say(note, "Math, physics, logic — creators decide what comes next.", 0.40);
pulse(work, 0.70);
wait(1.80);
quadratic-formula-continuity
The quadratic formula by completing the square with one persistent LaTeX equation.
Each authored rewrite retains unchanged symbols, moves reused terms, and introduces
only the new notation — the acceptance benchmark for structured formula motion.
// quadratic-formula-continuity.manic — structured LaTeX rewrite benchmark
//
// The author supplies correct mathematical states. `rewrite` does not solve
// algebra: it keeps equal RaTeX parts alive, moves them to their new roles,
// introduces only new notation, and settles on the exact target formula.
title("The Quadratic Formula by Completing the Square");
canvas("16:9");
template("plain");
watermark(manicMark, (1135, 30), "Made With Manic");
text(kicker, (cx, 72), "COMPLETING THE SQUARE · ONE CONTINUOUS EQUATION");
size(kicker, 18); color(kicker, dim); bold(kicker); hidden(kicker);
text(guide, (cx, 620), "Stable symbols stay. Only the mathematical change moves.");
size(guide, 19); color(guide, dim); hidden(guide);
equation(
work,
(cx, 330),
`\textcolor{orange}{a}x^2+\textcolor{cyan}{b}x+\textcolor{lime}{c}=0`,
48
);
hidden(work);
show(kicker, 0.45);
show(guide, 0.45);
show(work, 0.65);
wait(1.20);
// Divide through by a.
rewrite(
work,
`x^2+\frac{\textcolor{cyan}{b}}{\textcolor{orange}{a}}x+\frac{\textcolor{lime}{c}}{\textcolor{orange}{a}}=0`,
1.00,
smooth
);
wait(1.00);
// Move c/a across the equality.
rewrite(
work,
`x^2+\frac{\textcolor{cyan}{b}}{\textcolor{orange}{a}}x=-\frac{\textcolor{lime}{c}}{\textcolor{orange}{a}}`,
0.95,
smooth
);
wait(0.95);
// Split the middle coefficient into two equal halves.
rewrite(
work,
`x^2+\frac{\textcolor{cyan}{b}}{2\textcolor{orange}{a}}x+\frac{\textcolor{cyan}{b}}{2\textcolor{orange}{a}}x=-\frac{\textcolor{lime}{c}}{\textcolor{orange}{a}}`,
1.05,
smooth
);
wait(0.95);
// Add the completing-square term to both sides.
rewrite(
work,
`x^2+\frac{\textcolor{cyan}{b}}{2\textcolor{orange}{a}}x+\frac{\textcolor{cyan}{b}}{2\textcolor{orange}{a}}x+\left(\frac{\textcolor{cyan}{b}}{2\textcolor{orange}{a}}\right)^2=\left(\frac{\textcolor{cyan}{b}}{2\textcolor{orange}{a}}\right)^2-\frac{\textcolor{lime}{c}}{\textcolor{orange}{a}}`,
1.10,
smooth
);
wait(1.05);
// Simplify the right side locally.
rewrite(
work,
`x^2+\frac{\textcolor{cyan}{b}}{2\textcolor{orange}{a}}x+\frac{\textcolor{cyan}{b}}{2\textcolor{orange}{a}}x+\left(\frac{\textcolor{cyan}{b}}{2\textcolor{orange}{a}}\right)^2=\frac{\textcolor{cyan}{b}^2-4\textcolor{orange}{a}\textcolor{lime}{c}}{4\textcolor{orange}{a}^2}`,
1.00,
smooth
);
wait(1.00);
// Expose the repeated factor, then contract it into a square.
rewrite(
work,
`x\left(x+\frac{\textcolor{cyan}{b}}{2\textcolor{orange}{a}}\right)+\frac{\textcolor{cyan}{b}}{2\textcolor{orange}{a}}\left(x+\frac{\textcolor{cyan}{b}}{2\textcolor{orange}{a}}\right)=\frac{\textcolor{cyan}{b}^2-4\textcolor{orange}{a}\textcolor{lime}{c}}{4\textcolor{orange}{a}^2}`,
1.05,
smooth
);
wait(0.90);
rewrite(
work,
`\left(x+\frac{\textcolor{cyan}{b}}{2\textcolor{orange}{a}}\right)^2=\frac{\textcolor{cyan}{b}^2-4\textcolor{orange}{a}\textcolor{lime}{c}}{4\textcolor{orange}{a}^2}`,
0.95,
smooth
);
wait(1.00);
// Take square roots.
rewrite(
work,
`x+\frac{\textcolor{cyan}{b}}{2\textcolor{orange}{a}}=\pm\frac{\sqrt{\textcolor{cyan}{b}^2-4\textcolor{orange}{a}\textcolor{lime}{c}}}{2\textcolor{orange}{a}}`,
1.05,
smooth
);
wait(1.10);
// Isolate x. The fraction moves across; the discriminant remains itself.
rewrite(
work,
`x=\frac{-\textcolor{cyan}{b}\pm\sqrt{\textcolor{cyan}{b}^2-4\textcolor{orange}{a}\textcolor{lime}{c}}}{2\textcolor{orange}{a}}`,
1.10,
smooth
);
pulse(work, 0.65);
wait(2.50);
transforms
Apply a 2x2 matrix (ApplyMatrix) to a group.
// Transforms — the "animate anything" showcase.
// Named verbs (rotate, spin, scale, move) plus the general `to(id, prop, value)`
// escape hatch, composed with par / seq / stagger.
//
// manic examples/transforms.manic
// manic examples/transforms.manic --record out --fps 60 --crt
title("Transforms");
canvas(1280, 720);
// cast
rect(box, (330, 400), 150, 150); outline(box, cyan);
label(box, "rotate");
rect(dia, (650, 400), 140, 140); outline(dia, magenta);
label(dia, "to");
circle(orb, (980, 400), 62); outline(orb, lime);
label(orb, "spin");
dot(p, (150, 620), 12);
text(cap, (640, 662), ""); color(cap, dim); size(cap, 22);
text(head, (640, 120), "animate anything");
display(head); color(head, cyan); size(head, 40); hidden(head);
// script
show(head, 0.5);
section("Named verbs");
say(cap, "rotate to an absolute angle");
rotate(box, 45, 0.7, overshoot);
say(cap, "spin by a relative angle, twice");
seq {
spin(orb, 180, 0.6);
spin(orb, 180, 0.6);
}
wait(0.4);
section("Animate anything");
say(cap, "to(id, property, value) reaches any property");
par {
to(dia, angle, 45, 0.6, smooth);
to(dia, scale, 1.4, 0.6);
to(dia, color, lime, 0.6);
}
wait(0.4);
say(cap, "compose freely with par / seq / stagger");
stagger(0.12) {
to(box, opacity, 0.4, 0.4);
to(dia, opacity, 0.4, 0.4);
to(orb, opacity, 0.4, 0.4);
}
to(p, x, 1130, 1.0, overshoot);
to(p, color, magenta, 0.4);
wait(1.0);
transform_copy
Duplicate an entity, then transform the copy.
// Copy + Winding Morph — two of the Transform family niceties. `copy(c, a)`
// duplicates a shape so the original stays while the copy transforms; `morph`
// with a spin angle winds the blend (Manim's Clockwise / Counterclockwise
// Transform). Left copy morphs clockwise, right copy counter-clockwise.
//
// manic examples/transform_copy.manic
title("Copy + Winding Morph");
canvas("16:9");
text(head, (cx, 96), "a copy morphs while the original stays -- one CW, one CCW");
color(head, cyan); size(head, 23); hidden(head);
// left: original circle (dim) + a cyan copy that morphs into a square, clockwise
circle(o1, (400, 380), 120); color(o1, dim); stroke(o1, 3); hidden(o1);
rect(t1, (400, 380), 220, 220); hidden(t1);
copy(c1, o1); color(c1, cyan); stroke(c1, 5); glow(c1, 1.6); hidden(c1);
morph(c1, t1, 200); // +200 deg = clockwise wind
// right: same idea, counter-clockwise into a triangle-ish (use another square)
circle(o2, (900, 380), 120); color(o2, dim); stroke(o2, 3); hidden(o2);
rect(t2, (900, 380), 220, 220); hidden(t2);
copy(c2, o2); color(c2, magenta); stroke(c2, 5); glow(c2, 1.6); hidden(c2);
morph(c2, t2, -200); // -200 deg = counter-clockwise
// --- script ---
show(head, 0.5);
par { show(o1, 0.4); show(o2, 0.4); show(c1, 0.4); show(c2, 0.4); }
wait(0.5);
section("Morph the copies");
par { to(c1, morph, 1, 1.8, smooth); to(c2, morph, 1, 1.8, smooth); }
wait(0.9);
par { to(c1, morph, 0, 1.8, smooth); to(c2, morph, 0, 1.8, smooth); }
wait(1.2);
morph
A sampled-point shape morph from A to B.
// Shape Morph — a circle's outline blends smoothly into a square's and back
// (Manim's Transform). `morph(a, b)` samples both outlines to the same number
// of points; `to(a, morph, t)` interpolates between them (t = 0 is `a`'s shape,
// 1 is `b`'s).
//
// manic examples/morph.manic
title("Shape Morph");
canvas("16:9");
text(head, (cx, 110), "a circle becomes a square -- and back");
display(head); color(head, cyan); size(head, 26); hidden(head);
circle(sh, (cx, cy), 150); color(sh, cyan); stroke(sh, 5); glow(sh, 1.6); hidden(sh);
rect(target, (cx, cy), 290, 290); hidden(target); // defines the square outline
morph(sh, target); // set sh up to morph into it
// --- script ---
show(head, 0.5);
show(sh, 0.6);
wait(0.5);
section("Morph");
to(sh, morph, 1, 1.6, smooth); // circle -> square
wait(0.7);
to(sh, morph, 0, 1.6, smooth); // square -> circle
wait(0.7);
to(sh, morph, 1, 1.1, overshoot); // and back, with a bounce
wait(1.4);
morph-icons
Morph imported svg() vectors: one emoji reshapes into another and back. A bare svg tag
resolves to its dominant path, filled Regions sample along their outer ring, and the two
outlines are winding-matched + rotated for a clean shape-to-shape blend — no tangle through
the centre. Paired with a colour tween so the heart warms to gold as it becomes a star.
// morph-icons.manic — morph imported SVG vectors from one shape into another.
//
// `morph(a, b)` samples both outlines and blends `a` into `b`'s shape; drive it
// with `to(a, morph, 1, dur)`. It now works on imported `svg()` art: a bare svg
// tag resolves to its dominant path, filled Regions sample along their outer ring,
// and the two loops are winding-matched + rotated for a clean shape-to-shape blend
// (no tangle through the centre). Here one emoji reshapes into another and back.
//
// manic examples/morph-icons.manic
title("Morph — imported vectors reshape");
canvas("16:9");
template("blank");
svg(heart, (cx, cy), "asset:svg/emoji/heart.svg", 260);
svg(star, (cx, cy), "asset:svg/emoji/star.svg", 260); hidden(star);
text(cap, (cx, h*0.86), "morph(heart, star) — one imported icon reshapes into another");
size(cap, 24); color(cap, dim); display(cap);
// set the heart up to morph into the star's outline
morph(heart, star);
seq {
wait(0.5);
par { to(heart, morph, 1, 1.6, smooth); to(heart, color, gold, 1.6); }
wait(0.5);
par { to(heart, morph, 0, 1.6, smooth); to(heart, color, magenta, 1.6); }
wait(0.5);
}
shapes-howto
SHAPECRAFT — Manic shape power in one film: outlined vs filled (the axes trap),
the cast with particles/travel, the full palette that becomes squares then triangles,
spin-wound morph, then one coral disc that splits into four independent colours,
reunites as one fill, and moves — beside two-colour boolean regions (A−B / B−A / A∩B).
Docs-in-motion for the Shapes chapter.
// ============================================================================
// shapes-howto.manic — SHAPECRAFT
// A power demo that also teaches the rules:
// fills hide constructions · outlined reveals · dashed rings for contacts
// circle/rect/polygon/sector fill by default · morph / become change identity
//
// manic examples/shapes-howto.manic
// manic examples/shapes-howto.manic --record out --preset studio
// ============================================================================
title("Shapecraft — Manic Shape Power");
canvas("16:9");
template("blank");
text(cap, (cx, h - 34), ""); size(cap, 22); color(cap, dim); display(cap);
text(head, (cx, 58), ""); size(head, 32); color(head, gold); display(head); bold(head);
// ---- stage energy ----
circle(well, (cx, cy), 420);
filled(well); gradient(well, panel, void, indigo, radial); opacity(well, 0.7);
spline(orbit, (180, 520), (340, 220), (640, 160), (940, 220), (1100, 520));
stroke(orbit, 2.5); gradient(orbit, dim, violet, teal, "curvature");
untraced(orbit); hidden(orbit); opacity(orbit, 0.55);
// ---- hero: one persistent outlined circle ----
circle(hero, (cx, cy), 120);
outlined(hero); color(hero, cyan); stroke(hero, 6); glow(hero, 1.4);
gradient(hero, cyan, mint, teal, 45);
untraced(hero); hidden(hero);
// fill veil — fades over the hero to show the trap without destroying morph
circle(veil, (cx, cy), 120);
filled(veil); color(veil, indigo); opacity(veil, 0.92); hidden(veil); z(veil, 5);
rect(asSquare, (cx, cy), 220, 220); hidden(asSquare);
polygon(asTri, (cx, 250), (cx - 140, 520), (cx + 140, 520)); hidden(asTri);
sector(asPie, (cx, cy), 130, -30, 300); color(asPie, magenta); hidden(asPie);
polygon(asDiamond, (cx, cy - 130), (cx + 130, cy), (cx, cy + 130), (cx - 130, cy));
color(asDiamond, gold); hidden(asDiamond);
circle(finalForm, (cx, cy), 130);
outlined(finalForm); color(finalForm, cyan); stroke(finalForm, 6);
gradient(finalForm, cyan, mint, violet, 90); glow(finalForm, 1.6); hidden(finalForm);
morph(hero, asSquare, 180); // spin-wound morph ready
text(heroTag, (cx, cy - 170), "one persistent id");
size(heroTag, 22); color(heroTag, fg); hidden(heroTag);
// ---- axes for the fill-trap punch ----
line(axX, (cx - 200, cy), (cx + 200, cy));
color(axX, dim); stroke(axX, 2); untraced(axX); hidden(axX);
line(axY, (cx, cy - 160), (cx, cy + 160));
color(axY, dim); stroke(axY, 2); untraced(axY); hidden(axY);
// ---- the cast (draw-on parade, then orbit travel) ----
circle(castC, (cx - 280, 280), 48);
outlined(castC); color(castC, teal); stroke(castC, 3.5); untraced(castC); hidden(castC);
rect(castR, (cx - 100, 250), 90, 70);
outlined(castR); color(castR, coral); stroke(castR, 3.5); untraced(castR); hidden(castR);
polygon(castP, (cx + 80, 230), (cx + 30, 310), (cx + 130, 310));
outlined(castP); color(castP, violet); stroke(castP, 3.5); untraced(castP); hidden(castP);
sector(castS, (cx + 260, 280), 52, -40, 240);
outlined(castS); color(castS, gold); stroke(castS, 3.5); untraced(castS); hidden(castS);
arc(castA, (cx - 220, 480), 50, -20, 250);
color(castA, mint); stroke(castA, 4); untraced(castA); hidden(castA);
arrow(castV, (cx + 40, 500), (cx + 180, 430));
color(castV, magenta); stroke(castV, 4); untraced(castV); hidden(castV);
line(castL, (cx + 240, 500), (cx + 340, 430));
color(castL, lime); stroke(castL, 4); untraced(castL); hidden(castL);
// sparks that ring the hero, then settle
rect(seed, (cx, cy), 60, 60); hidden(seed);
particles(sparks, seed, 12, 6, 19, "random");
color(sparks, cyan); glow(sparks, 0.5); hidden(sparks);
circle(orbitRing, (cx, cy), 230); hidden(orbitRing);
// ---- palette chips (fill OK — colour IS the subject) ----
let py = 400;
circle(chip0, (140, py), 26); filled(chip0); color(chip0, fg); hidden(chip0);
circle(chip1, (220, py), 26); filled(chip1); color(chip1, cyan); hidden(chip1);
circle(chip2, (300, py), 26); filled(chip2); color(chip2, magenta); hidden(chip2);
circle(chip3, (380, py), 26); filled(chip3); color(chip3, lime); hidden(chip3);
circle(chip4, (460, py), 26); filled(chip4); color(chip4, gold); hidden(chip4);
circle(chip5, (540, py), 26); filled(chip5); color(chip5, red); hidden(chip5);
circle(chip6, (620, py), 26); filled(chip6); color(chip6, orange); hidden(chip6);
circle(chip7, (700, py), 26); filled(chip7); color(chip7, blue); hidden(chip7);
circle(chip8, (780, py), 26); filled(chip8); color(chip8, teal); hidden(chip8);
circle(chip9, (860, py), 26); filled(chip9); color(chip9, violet); hidden(chip9);
circle(chip10, (940, py), 26); filled(chip10); color(chip10, coral); hidden(chip10);
circle(chip11, (1020,py), 26); filled(chip11); color(chip11, indigo); hidden(chip11);
circle(chip12, (1100,py), 26); filled(chip12); color(chip12, mint); hidden(chip12);
circle(chip13, (1180,py), 26); filled(chip13); color(chip13, dim); hidden(chip13);
rect(sq0, (140, py), 48, 48); color(sq0, fg); filled(sq0); hidden(sq0);
rect(sq1, (220, py), 48, 48); color(sq1, cyan); filled(sq1); hidden(sq1);
rect(sq2, (300, py), 48, 48); color(sq2, magenta); filled(sq2); hidden(sq2);
rect(sq3, (380, py), 48, 48); color(sq3, lime); filled(sq3); hidden(sq3);
rect(sq4, (460, py), 48, 48); color(sq4, gold); filled(sq4); hidden(sq4);
rect(sq5, (540, py), 48, 48); color(sq5, red); filled(sq5); hidden(sq5);
rect(sq6, (620, py), 48, 48); color(sq6, orange); filled(sq6); hidden(sq6);
rect(sq7, (700, py), 48, 48); color(sq7, blue); filled(sq7); hidden(sq7);
rect(sq8, (780, py), 48, 48); color(sq8, teal); filled(sq8); hidden(sq8);
rect(sq9, (860, py), 48, 48); color(sq9, violet); filled(sq9); hidden(sq9);
rect(sq10, (940, py), 48, 48); color(sq10, coral); filled(sq10); hidden(sq10);
rect(sq11, (1020,py), 48, 48); color(sq11, indigo); filled(sq11); hidden(sq11);
rect(sq12, (1100,py), 48, 48); color(sq12, mint); filled(sq12); hidden(sq12);
rect(sq13, (1180,py), 48, 48); color(sq13, dim); filled(sq13); hidden(sq13);
polygon(tri0, (140, py-22), (114, py+22), (166, py+22)); color(tri0, fg); filled(tri0); hidden(tri0);
polygon(tri1, (220, py-22), (194, py+22), (246, py+22)); color(tri1, cyan); filled(tri1); hidden(tri1);
polygon(tri2, (300, py-22), (274, py+22), (326, py+22)); color(tri2, magenta); filled(tri2); hidden(tri2);
polygon(tri3, (380, py-22), (354, py+22), (406, py+22)); color(tri3, lime); filled(tri3); hidden(tri3);
polygon(tri4, (460, py-22), (434, py+22), (486, py+22)); color(tri4, gold); filled(tri4); hidden(tri4);
polygon(tri5, (540, py-22), (514, py+22), (566, py+22)); color(tri5, red); filled(tri5); hidden(tri5);
polygon(tri6, (620, py-22), (594, py+22), (646, py+22)); color(tri6, orange); filled(tri6); hidden(tri6);
polygon(tri7, (700, py-22), (674, py+22), (726, py+22)); color(tri7, blue); filled(tri7); hidden(tri7);
polygon(tri8, (780, py-22), (754, py+22), (806, py+22)); color(tri8, teal); filled(tri8); hidden(tri8);
polygon(tri9, (860, py-22), (834, py+22), (886, py+22)); color(tri9, violet); filled(tri9); hidden(tri9);
polygon(tri10, (940, py-22), (914, py+22), (966, py+22)); color(tri10, coral); filled(tri10); hidden(tri10);
polygon(tri11, (1020,py-22),(994, py+22),(1046,py+22)); color(tri11, indigo); filled(tri11); hidden(tri11);
polygon(tri12, (1100,py-22),(1074,py+22),(1126,py+22)); color(tri12, mint); filled(tri12); hidden(tri12);
polygon(tri13, (1180,py-22),(1154,py+22),(1206,py+22)); color(tri13, dim); filled(tri13); hidden(tri13);
// ---- CENTER: one filled disc → 4 independent colours → one colour → move ----
circle(disc, (cx, cy), 140);
filled(disc); color(disc, coral); outline(disc, cyan); stroke(disc, 4);
glow(disc, 0.6); hidden(disc);
pie(q, (cx, cy), 140, 4); // q0..q3 — same centre/radius as disc
color(q0, teal); filled(q0); outline(q0, fg); stroke(q0, 2); untraced(q0); hidden(q0);
color(q1, coral); filled(q1); outline(q1, fg); stroke(q1, 2); untraced(q1); hidden(q1);
color(q2, violet); filled(q2); outline(q2, fg); stroke(q2, 2); untraced(q2); hidden(q2);
color(q3, gold); filled(q3); outline(q3, fg); stroke(q3, 2); untraced(q3); hidden(q3);
// reunited whole — one colour, one id that can move
circle(whole, (cx, cy), 140);
filled(whole); color(whole, cyan); outline(whole, mint); stroke(whole, 4);
glow(whole, 0.8); hidden(whole);
// ---- RIGHT: boolean → TWO regions, TWO colours ----
let bx = 1000;
let by = 400;
rect(bBox, (bx - 35, by - 35), 130, 130);
outlined(bBox); outline(bBox, cyan); opacity(bBox, 0.4); hidden(bBox);
circle(bCirc, (bx + 40, by + 20), 78);
outlined(bCirc); outline(bCirc, magenta); opacity(bCirc, 0.4); hidden(bCirc);
difference(onlyBox, bBox, bCirc, teal); hidden(onlyBox); glow(onlyBox, 0.6);
difference(onlyCirc, bCirc, bBox, coral); hidden(onlyCirc); glow(onlyCirc, 0.6);
intersect(bHit, bBox, bCirc, gold); hidden(bHit); glow(bHit, 0.5);
text(labBool, (bx, by + 155), "A−B · B−A · A∩B"); size(labBool, 16); color(labBool, dim); hidden(labBool);
// ---- contact truth ----
line(crossH, (cx - 160, cy), (cx + 160, cy));
color(crossH, dim); stroke(crossH, 2.5); dashed(crossH, 12, 8); untraced(crossH); hidden(crossH);
line(crossV, (cx, cy - 120), (cx, cy + 120));
color(crossV, dim); stroke(crossV, 2.5); dashed(crossV, 12, 8); untraced(crossV); hidden(crossV);
dot(badDot, (cx, cy), 16); color(badDot, coral); glow(badDot, 0.5); hidden(badDot);
circle(goodRing, (cx, cy), 18);
outlined(goodRing); color(goodRing, mint); stroke(goodRing, 2.8); dashed(goodRing, 4, 3);
untraced(goodRing); hidden(goodRing);
// ---- traveler on the orbit ----
dot(rider, (180, 520), 9); color(rider, gold); glow(rider, 1.2); hidden(rider);
// ============================================================================
// SCRIPT — one continuous shapecraft story
// ============================================================================
step("birth") {
seq {
say(head, "SHAPECRAFT", 0.35);
say(cap, "one shape. every identity. the visual stays true.", 0.35);
show(hero, 0.05);
par {
draw(hero, 1.2, smooth);
zoom(1.15, 1.2, smooth);
}
attach(heroTag, hero, (0, -160));
show(heroTag, 0.35);
wait(0.35);
}
}
step("truth") {
seq {
say(head, "outlined — constructions stay readable", 0.3);
say(cap, "axes through the hero: the rim never hides the truth.", 0.3);
par {
show(axX, 0.05); show(axY, 0.05);
draw(axX, 0.55); draw(axY, 0.55);
cam((cx, cy), 0.8, smooth); zoom(1.35, 0.8, smooth);
}
pulse(hero);
wait(0.45);
say(head, "filled — the trap", 0.25);
say(cap, "a filled disc sits on the axes and deletes them visually.", 0.25);
show(veil, 0.45);
flash(veil, coral);
wait(0.7);
say(head, "outlined — axes return", 0.25);
say(cap, "outline(id, c) only recolors the rim. outlined drops the fill.", 0.25);
fade(veil, 0.45);
flash(hero, mint);
wait(0.55);
par {
fade(axX, 0.35); fade(axY, 0.35); fade(heroTag, 0.35); fade(hero, 0.35);
cam((cx, cy), 0.7, smooth); zoom(1.0, 0.7, smooth);
}
}
}
step("cast") {
seq {
say(head, "the cast", 0.3);
say(cap, "circle · rect · polygon · sector · arc · line · arrow", 0.3);
show(sparks, 0.25);
arrange(sparks, orbitRing, "ring", 1.0, smooth);
wait(0.2);
stagger(0.1) {
show(castC, 0.05); draw(castC, 0.55);
show(castR, 0.05); draw(castR, 0.55);
show(castP, 0.05); draw(castP, 0.55);
show(castS, 0.05); draw(castS, 0.55);
show(castA, 0.05); draw(castA, 0.55);
show(castV, 0.05); draw(castV, 0.55);
show(castL, 0.05); draw(castL, 0.55);
}
wait(0.2);
show(orbit, 0.05); draw(orbit, 1.0, smooth);
show(rider, 0.2);
travel(rider, orbit, 2.0, smooth);
wait(0.25);
par {
fade(sparks, 0.4);
fade(castC, 0.4); fade(castR, 0.4); fade(castP, 0.4); fade(castS, 0.4);
fade(castA, 0.4); fade(castV, 0.4); fade(castL, 0.4);
fade(orbit, 0.4); fade(rider, 0.4);
}
}
}
step("palette") {
seq {
say(head, "the whole palette — then change the shape", 0.3);
say(cap, "filled chips when colour is the subject. become keeps the id.", 0.3);
stagger(0.05) {
show(chip0, 0.22); show(chip1, 0.22); show(chip2, 0.22); show(chip3, 0.22);
show(chip4, 0.22); show(chip5, 0.22); show(chip6, 0.22); show(chip7, 0.22);
show(chip8, 0.22); show(chip9, 0.22); show(chip10, 0.22); show(chip11, 0.22);
show(chip12, 0.22); show(chip13, 0.22);
}
wait(0.35);
say(cap, "become → square", 0.2);
stagger(0.035) {
for i in 0..14 { become(chip{i}, sq{i}, 0.65, smooth); }
}
wait(0.25);
say(cap, "become → triangle", 0.2);
stagger(0.035) {
for i in 0..14 { become(chip{i}, tri{i}, 0.65, smooth); }
}
wait(0.45);
par {
for i in 0..14 { fade(chip{i}, 0.35); }
}
}
}
step("morph") {
seq {
say(head, "morph · become · spin", 0.3);
say(cap, "same hero id — circle winds into a square, then becomes new geometry.", 0.3);
show(hero, 0.3);
to(hero, morph, 1, 1.5, smooth); // → square (with 180° wind)
wait(0.25);
become(hero, asDiamond, 0.95, smooth);
pulse(hero);
wait(0.2);
become(hero, asPie, 0.95, smooth);
spin(hero, 40, 0.7, out);
wait(0.2);
become(hero, asTri, 0.9, smooth);
wait(0.45);
}
}
step("pieces") {
seq {
say(head, "one fill → four fills → one fill → move", 0.3);
say(cap, "a filled disc can split into independent colours, reunite, then travel as one.", 0.3);
fade(hero, 0.3);
// 1 — the coral disc (same look as the fill-trap)
par {
show(axX, 0.05); show(axY, 0.05);
draw(axX, 0.4); draw(axY, 0.4);
cam((cx, cy), 0.6, smooth); zoom(1.25, 0.6, smooth);
}
show(disc, 0.45);
flash(disc, coral);
wait(0.5);
// 2 — cut into 4 independent colours
say(cap, "cut: each quadrant keeps its own fill.", 0.25);
fade(disc, 0.3);
stagger(0.1) {
show(q0, 0.05); draw(q0, 0.45);
show(q1, 0.05); draw(q1, 0.45);
show(q2, 0.05); draw(q2, 0.45);
show(q3, 0.05); draw(q3, 0.45);
}
// explode slightly so the independence is obvious
par {
move(q0, (cx + 22, cy - 22), 0.45, overshoot);
move(q1, (cx - 22, cy - 22), 0.45, overshoot);
move(q2, (cx - 22, cy + 22), 0.45, overshoot);
move(q3, (cx + 22, cy + 22), 0.45, overshoot);
}
wait(0.45);
// 3 — reunite as ONE colour, then ONE circle
say(cap, "reunite: every slice takes one colour, then they become one disc.", 0.25);
par {
recolor(q0, cyan, 0.5);
recolor(q1, cyan, 0.5);
recolor(q2, cyan, 0.5);
recolor(q3, cyan, 0.5);
}
// snap slices back to centre
par {
move(q0, (cx, cy), 0.5, smooth);
move(q1, (cx, cy), 0.5, smooth);
move(q2, (cx, cy), 0.5, smooth);
move(q3, (cx, cy), 0.5, smooth);
}
wait(0.15);
// swap the four pieces for one solid disc (same colour)
par {
fade(q0, 0.3); fade(q1, 0.3); fade(q2, 0.3); fade(q3, 0.3);
show(whole, 0.35);
}
pulse(whole);
wait(0.4);
// 4 — move as one unit
say(cap, "now it moves as a single identity.", 0.25);
par {
fade(axX, 0.35); fade(axY, 0.35);
cam((cx + 120, cy), 0.9, smooth); zoom(1.05, 0.9, smooth);
move(whole, (cx + 260, cy), 1.1, smooth);
}
wait(0.35);
// side boolean still lands on the right as the disc arrives
say(head, "boolean keeps two colours", 0.25);
say(cap, "A−B teal · B−A coral · then A∩B gold — never one muddy fill.", 0.25);
par { show(bBox, 0.3); show(bCirc, 0.3); }
wait(0.3);
par { fade(bBox, 0.2); fade(bCirc, 0.2); }
par { show(onlyBox, 0.4); show(onlyCirc, 0.4); }
flash(onlyBox, teal); flash(onlyCirc, coral);
show(labBool, 0.25);
wait(0.4);
show(bHit, 0.45); flash(bHit, gold);
wait(0.7);
par {
fade(whole, 0.35);
fade(onlyBox, 0.35); fade(onlyCirc, 0.35); fade(bHit, 0.35); fade(labBool, 0.35);
cam((cx, cy), 0.6, smooth); zoom(1.0, 0.6, smooth);
}
}
}
step("contact") {
seq {
say(head, "contact points", 0.3);
say(cap, "a solid dot hides the crossing. a dashed ring keeps it.", 0.3);
par {
show(crossH, 0.05); show(crossV, 0.05);
draw(crossH, 0.5); draw(crossV, 0.5);
cam((cx, cy), 0.7, smooth); zoom(1.55, 0.7, smooth);
}
show(badDot, 0.3);
flash(badDot, coral);
wait(0.55);
fade(badDot, 0.25);
show(goodRing, 0.05); draw(goodRing, 0.55);
pulse(goodRing);
wait(0.7);
par {
fade(crossH, 0.35); fade(crossV, 0.35); fade(goodRing, 0.35);
cam((cx, cy), 0.7, smooth); zoom(1.0, 0.7, smooth);
}
}
}
step("settle") {
seq {
say(head, "fills for subjects. outlines for truth.", 0.35);
say(cap, "shapecraft: the visual is true — never hide what you're proving.", 0.35);
show(hero, 0.2);
become(hero, finalForm, 1.0, smooth);
pulse(hero);
wait(1.6);
}
}