3D scenes
Each block is the whole file — copy it into x.manic and run manic x.manic (live) or --record out (video). See the Going 3D chapter for the words used here. three-d-v2-lab uses the bundled geometry-only asset asset:models/manic-pyramid.obj; production packages install it automatically, or you can replace its model3 call with a built-in solid.
wireframe-spheres
A drifting field of triangulated wireframe spheres, each breathe-ing with a per-index phase so the
field pulses like soap bubbles. finish3(id,"wire=1") renders any solid as bright see-through edges.
// wireframe-spheres — a drifting field of triangulated wireframe spheres, each
// BREATHING (radius oscillating) with a phase tied to its index, so the field
// pulses like a shoal of soap bubbles. The breathing is the 3-D echo of the 2-D
// `wheel-radial` wave; `finish3(id, "wire=1")` renders any solid as its bright
// triangulated wireframe with no filled faces (works on cube/prism/pyramid too).
//
// manic examples/wireframe-spheres.manic
canvas(1080, 1080);
template("black");
camera3((0, -15, 1.5), (0, 0, 0), 56);
let m = 66;
for i in 0..m {
let x = 9.5 * sin(i * 2.399 + 0.5);
let y = 8.5 * sin(i * 1.13 + 1.7);
let z = 6.5 * sin(i * 3.11 + 0.3);
let r = 0.5 + 0.85 * sin(i * 1.7) * sin(i * 1.7);
sphere3(s{i}, (x, y, z), r);
finish3(s{i}, "wire=1");
hue(s{i}, 360 * i / m); // a computed rainbow — hue works on 3D now
}
// every sphere breathes at once, but each with its OWN amplitude, period and
// phase (deterministic pseudo-random from the index — manic has no rand()), so
// the bubbles pulse by different amounts instead of in lockstep.
par {
for i in 0..m {
let ra = 0.5 + 0.5 * sin(i * 12.9 + 0.5); // 0..1 pseudo-random
let rb = 0.5 + 0.5 * sin(i * 7.7 + 2.3);
let rc = 0.5 + 0.5 * sin(i * 5.3 + 4.1);
let amp = 0.28 + 0.55 * ra * ra; // breath size: ~0.28..0.83
let per = 2.0 + 2.2 * rb; // breath rate: 2.0..4.2 s
breathe(s{i}, per, amp, rc, 14);
}
}
three-d-v2
The compact 3D V2 reference: frame a tagged craft, attach its parts, travel one persistent subject along a spatial route, deploy the rig with a rigid turn, and become the final blueprint — five creator words in one continuous scene.
// three-d-v2.manic — the five creator-first spatial motion words
title("3D V2 — Five Words for Spatial Stories");
canvas("16:9");
template("shorts");
watermark(manicMark, (126, 64), "Made With Manic");
creator(me, "@anish2good name=Manic_3D tagline=Spatial_stories yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=cyan secondary=magenta footer=social cta=Create_yours safe=clean");
socials(me);
camera3((10, -13, 8), (0, 0, 1), 42);
grid3(floor, (0, 0, 0), 6, 1); color(floor, dim); opacity(floor, 0.24);
// A route can be a line3, arrow3, or curve3.
curve3(route, "-4 + 8*t", "-2 + 3*sin(pi*t)", "0.8 + 3.2*sin(pi*t)", (0, 1));
color(route, cyan); thick(route, 0.035); untraced(route);
cube3(ship, (-4, -2, 0.8), (1.4, 1.0, 0.65)); color(ship, gold);
sphere3(sensor, (-4, -2, 1.65), 0.34); color(sensor, cyan);
cube3(leftWing, (-4, -3.15, 0.8), (0.18, 1.8, 0.10)); color(leftWing, magenta);
cube3(rightWing, (-4, -0.85, 0.8), (0.18, 1.8, 0.10)); color(rightWing, magenta);
tag(ship, spacecraft); tag(sensor, spacecraft);
tag(leftWing, spacecraft); tag(rightWing, spacecraft);
tag(leftWing, wings); tag(rightWing, wings);
// A hidden target is a blueprint. become3 keeps `ship` as the persistent id.
sphere3(observatoryBlueprint, (4, -2, 0.95), 0.88);
color(observatoryBlueprint, lime); hidden(observatoryBlueprint);
text(caption, (640, 622), "FRAME · FOLLOW · TRAVEL · TRANSFORM · TURN");
size(caption, 24); bold(caption); color(caption, fg); hidden(caption);
step("frame-the-route") {
par {
view3(route, "fit", 1.0, smooth, 1.30);
show(caption, 0.45);
}
}
step("travel-as-one-system") {
attach3(sensor, ship, (0, 0, 0.85));
attach3(leftWing, ship, (0, -1.15, 0));
attach3(rightWing, ship, (0, 1.15, 0));
par {
draw(route, 2.8, smooth);
travel3(ship, route, 2.8, smooth);
say(caption, "attach3 + travel3", 0.55, smooth);
}
}
step("deploy-and-transform") {
seq {
attach3(sensor, none);
attach3(leftWing, none);
attach3(rightWing, none);
par {
turn3(wings, ship, z, 90, 1.1, smooth);
become3(ship, observatoryBlueprint, 1.1, smooth);
say(caption, "turn3 + become3", 0.55, smooth);
}
par {
view3(spacecraft, "isometric", 1.2, smooth, 1.35);
say(caption, "view3 settles the final composition", 0.55, smooth);
fade(route, 0.65);
}
wait(1.2);
}
}
three-d-v2-story
A vertical creator story about a satellite finding orbit. The same mission survives assembly, launch, transformation, deployment, and screen-aware camera composition without a scene reset.
// three-d-v2-story.manic — a vertical creator story built on 3D V2
title("From Signal to Satellite — A 3D Motion Story");
canvas("9:16");
template("shorts");
watermark(manicMark, (164, 86), "Made With Manic");
creator(me, "@anish2good name=Manic_3D tagline=Make_space_understandable yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=cyan secondary=magenta footer=social cta=Create_yours safe=reels");
socials(me);
text(kicker, (540, 164), "A 3D MOTION STORY");
size(kicker, 22); bold(kicker); color(kicker, cyan); hidden(kicker);
text(headline, (540, 226), "How a satellite finds its orbit");
size(headline, 39); bold(headline); color(headline, fg); hidden(headline);
text(chapter, (540, 300), "ACT 1 · BUILD ONE SYSTEM");
size(chapter, 23); bold(chapter); color(chapter, gold); hidden(chapter);
text(narration, (540, 1472), "Three parts. One mission.");
size(narration, 30); bold(narration); color(narration, fg); hidden(narration);
text(principle, (540, 1544), "Spatial motion stays readable when relationships survive the move.");
size(principle, 22); color(principle, dim); wrap(principle, 820); hidden(principle);
camera3((10, -14, 8), (0, 0, 1), 42);
grid3(floor, (0, 0, 0), 7, 1); color(floor, dim); opacity(floor, 0.20);
curve3(launchRoute, "-4 + 7*t", "-2 + 2.6*sin(pi*t)", "0.9 + 4.1*sin(pi*t)", (0, 1));
color(launchRoute, cyan); thick(launchRoute, 0.045); untraced(launchRoute);
cube3(core, (-4, -2, 0.9), (1.25, 0.95, 0.72)); color(core, gold); hidden(core);
sphere3(lens, (-4, -2, 1.78), 0.34); color(lens, cyan); hidden(lens);
cube3(panelA, (-4, -3.30, 0.9), (0.18, 2.1, 0.10)); color(panelA, magenta); hidden(panelA);
cube3(panelB, (-4, -0.70, 0.9), (0.18, 2.1, 0.10)); color(panelB, magenta); hidden(panelB);
tag(core, mission); tag(lens, mission); tag(panelA, mission); tag(panelB, mission);
tag(panelA, solarArray); tag(panelB, solarArray);
// The final observatory is only a blueprint. It never appears as a second object.
sphere3(observatory, (3, -2, 1.05), 0.86); color(observatory, lime); hidden(observatory);
text(lensLabel, (0, 0), "signal"); size(lensLabel, 21); color(lensLabel, cyan); hidden(lensLabel);
pin3(lensLabel, lens);
step("assemble") {
seq {
par {
show(kicker, 0.35);
show(headline, 0.50);
show(chapter, 0.35);
show(narration, 0.40);
show(principle, 0.40);
view3(mission, "isometric", 1.0, smooth, 1.55);
}
stagger(0.12) {
show(core, 0.35);
show(lens, 0.35);
show(panelA, 0.35);
show(panelB, 0.35);
}
attach3(lens, core, (0, 0, 0.88));
attach3(panelA, core, (0, -1.30, 0));
attach3(panelB, core, (0, 1.30, 0));
show(lensLabel, 0.30);
wait(0.45);
}
}
step("launch") {
seq {
par {
say(chapter, "ACT 2 · MOVE THE RELATIONSHIP", 0.45, smooth);
say(narration, "The craft travels. Every attached part remembers where it belongs.", 0.55, smooth);
view3(launchRoute, "fit", 1.1, smooth, 1.48);
}
par {
draw(launchRoute, 3.2, smooth);
travel3(core, launchRoute, 3.2, smooth);
say(principle, "attach3 carries context · travel3 carries identity", 0.55, smooth);
}
wait(0.45);
}
}
step("deploy") {
seq {
attach3(lens, none);
attach3(panelA, none);
attach3(panelB, none);
par {
say(chapter, "ACT 3 · DEPLOY THE IDEA", 0.45, smooth);
say(narration, "At orbit, only the changing parts move.", 0.50, smooth);
turn3(solarArray, core, z, 90, 1.25, smooth);
become3(core, observatory, 1.25, smooth);
}
par {
view3(mission, "isometric", 1.4, smooth, 1.58);
say(principle, "turn3 preserves the rig · become3 preserves the subject", 0.55, smooth);
fade(launchRoute, 0.65);
}
par {
view3(mission, "front", 2.2, smooth, 1.24);
pulse(lens, 0.75);
say(narration, "One persistent object. One continuous story.", 0.55, smooth);
}
wait(1.8);
}
}
three-d-v2-lab
A creator-first spatial lab: safe-aware framing, rigid assembly, a live projection and edge, a moving route, surface contour, depth-scaled label, bounded finishes, variable tube, and controlled OBJ geometry in one continuous story.
canvas(1080, 1920);
template("neon");
title("3D relationships, not keyframes");
creator(me, "@anish2good name=Manic yt=zarigatongy x=@anish2good web=8gwifi.org/manic footer=social safe=reels");
socials(me);
watermark(mark, (180, 88), "Made With Manic");
camera3((8, -10, 7), (0, 0, 1), 42);
grid3(floor, (0, 0, 0), 4, 1); color(floor, dim); opacity(floor, 0.28);
text(kicker, (540, 174), "SPATIAL STORY"); size(kicker, 22); color(kicker, cyan);
text(headline, (540, 242), "One point. Three relationships."); size(headline, 46);
text(caption, (540, 1350), "Move the idea — the construction follows."); size(caption, 27); color(caption, dim);
surface3(bowl, "0.16*(x^2+y^2)", (-3,3), (-3,3), 24);
color(bowl, blue); opacity(bowl, 0.42);
finish3(bowl, "shading=smooth mesh=0.18 depth=0.25 shadow=0.15");
contour3(level, bowl, 0.7); color(level, gold); thick(level, 0.025);
model3(beacon, "asset:models/manic-pyramid.obj", (-1.5, -0.4, 0.05), 0.8);
color(beacon, magenta);
finish3(beacon, "material=metal shading=flat mesh=0.20 depth=0.18 shadow=0.30");
point3(probe, (0, 0, 0), 0.15); color(probe, lime);
attach3(probe, beacon, (0, 0, 2.0), rigid);
project3(shadow, probe, "xy"); color(shadow, cyan);
link3(drop, probe, shadow, 0.12); color(drop, cyan); thick(drop, 0.018);
text(probeLabel, (0,0), "live point"); size(probeLabel, 28); color(probeLabel, lime);
label3(probeLabel, probe, 0.34);
curve3(route, "3*cos(pi*t)", "2.4*sin(pi*t)", "0.8+1.4*t", (0,1));
color(route, gold); untraced(route);
curve3(spine, "-2.8+1.2*t", "-2.2", "0.15+1.8*t", (0,1)); hidden(spine);
tube3(signal, spine, "0.025+0.075*t", 10); color(signal, gold); opacity(signal, 0.58);
finish3(signal, "material=metal shading=smooth depth=0.20"); hidden(signal);
tag(bowl, lab); tag(level, lab); tag(beacon, lab); tag(probe, lab);
tag(shadow, lab); tag(drop, lab); tag(route, lab); tag(signal, lab);
tag(spine, lab);
step("frame the safe media area") {
par {
show(kicker, 0.35);
show(headline, 0.55);
show(caption, 0.45);
view3(lab, "isometric", 1.1, smooth, 1.55);
}
wait(0.5);
}
step("rigid attachment") {
par {
turn3(beacon, beacon, z, 110, 1.5, smooth);
orbit3(42, 28, 10.8, 1.5, smooth);
}
wait(0.35);
}
step("live projection") {
par {
shift3(beacon, (2.4, 0.8, 0.3), 1.5, smooth);
look3((0.4, 0.2, 1.1), 1.5, smooth);
}
wait(0.35);
}
step("transformed path travel") {
attach3(probe, none);
par {
draw(route, 2.2, smooth);
travel3(probe, route, 2.2, smooth);
rotate3(route, (0, 0, 24), 2.2, smooth);
show(signal, 0.55);
}
wait(0.7);
}
step("one bounded finish") {
flash(level, gold);
view3(lab, "fit", 1.0, smooth, 1.55);
wait(1.0);
}
creator-coordinate-worlds
Solve the classic 3 × 4 × 12 cuboid diagonal as a complete creator story: isolate the 3 × 4 floor in native 2D, reuse its 5-unit diagonal in textbook-projected 3D, derive the 13-unit answer, then orbit the same retained frame3 in spatial 3D.
// A complete textbook problem told with three truthful presentations:
// 2D isolates the useful right triangle, textbook 3D builds the cuboid cleanly,
// and spatial 3D confirms that the final segment really crosses the volume.
title("The 3–4–12 Box — Find the Space Diagonal");
canvas("9:16");
template("paper");
creator(me,"@anish2good name=Manic_Geometry tagline=Textbooks_made_visible yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=cyan secondary=magenta footer=compact safe=clean");
socials(me);
watermark(manicMark,(w*0.17,h*0.065),"Made With Manic");
endcard(me,"title=Make_Math_Move cta=8gwifi.org/manic");
let u = (w+h-abs(w-h))/2160;
text(kicker,(cx,h*0.075),"MANIC · PYTHAGORAS IN 3D"); size(kicker,22*u); bold(kicker); color(kicker,cyan); hidden(kicker);
text(headline,(cx,h*0.13),"A box is 3 × 4 × 12. Find its space diagonal."); size(headline,36*u); bold(headline); wrap(headline,w*0.82); hidden(headline);
text(chapter,(cx,h*0.205),"THE PROBLEM"); size(chapter,23*u); bold(chapter); color(chapter,gold); hidden(chapter);
text(caption,(cx,h*0.805),"Connect one corner to the opposite corner—through the box."); size(caption,24*u); bold(caption); wrap(caption,w*0.78); hidden(caption);
text(note,(cx,h*0.855),"Hint: solve one right triangle at a time."); size(note,21*u); color(note,dim); wrap(note,w*0.78); hidden(note);
equation(work,(cx,h*0.735),`D=?`,43*u); hidden(work);
// ---------------------------------------------------------------------------
// 2D: isolate the 3 × 4 floor. This is the truthful view for the first triangle.
// ---------------------------------------------------------------------------
let lx = w*0.24;
let rx = w*0.76;
let ty = h*0.36;
let by = h*0.60;
line(floorBottom,(lx,by),(rx,by)); color(floorBottom,cyan); stroke(floorBottom,4*u); untraced(floorBottom); hidden(floorBottom); tag(floorBottom,floor2d);
line(floorRight,(rx,by),(rx,ty)); color(floorRight,magenta); stroke(floorRight,4*u); untraced(floorRight); hidden(floorRight); tag(floorRight,floor2d);
line(floorTop,(rx,ty),(lx,ty)); color(floorTop,cyan); stroke(floorTop,4*u); untraced(floorTop); hidden(floorTop); tag(floorTop,floor2d);
line(floorLeft,(lx,ty),(lx,by)); color(floorLeft,magenta); stroke(floorLeft,4*u); untraced(floorLeft); hidden(floorLeft); tag(floorLeft,floor2d);
line(floorDiagonal2,(lx,by),(rx,ty)); color(floorDiagonal2,gold); stroke(floorDiagonal2,6*u); untraced(floorDiagonal2); hidden(floorDiagonal2); tag(floorDiagonal2,floor2d);
dot(A2,(lx,by),9*u); filled(A2); color(A2,fg); hidden(A2); tag(A2,floor2d);
dot(C2,(rx,ty),9*u); filled(C2); color(C2,fg); hidden(C2); tag(C2,floor2d);
text(a2,(lx-22*u,by+24*u),"A"); size(a2,22*u); bold(a2); hidden(a2); tag(a2,floor2d);
text(c2,(rx+22*u,ty-20*u),"C"); size(c2,22*u); bold(c2); hidden(c2); tag(c2,floor2d);
text(side4,(cx,by+32*u),"4"); size(side4,24*u); bold(side4); color(side4,cyan); hidden(side4); tag(side4,floor2d);
text(side3,(rx+30*u,(ty+by)/2),"3"); size(side3,24*u); bold(side3); color(side3,magenta); hidden(side3); tag(side3,floor2d);
text(base5,(cx+22*u,(ty+by)/2-18*u),"5"); size(base5,25*u); bold(base5); color(base5,gold); hidden(base5); tag(base5,floor2d);
// ---------------------------------------------------------------------------
// 3D: the scientific frame retains the real 0..4, 0..3, 0..12 coordinates.
// Its display cube is intentionally balanced; the numbered axes keep the data
// honest while the space-diagonal geometry stays readable on a phone.
// ---------------------------------------------------------------------------
camera3((9,-11,8),(0,0,0),17,orthographic);
frame3(boxFrame,(0,0,0),(6,6,6),"x=0..4 y=0..3 z=0..12 xmajor=1 ymajor=1 zmajor=4 planes=xy:min,xz:min,yz:min mode=textbook");
hidden(boxFrame);
// Data-to-display corners of frame3:
// (0,0,0) -> (-3,-3,-3), (4,3,0) -> (3,3,-3), (4,3,12) -> (3,3,3).
point3(A3,(-3,-3,-3),0.15); color(A3,fg); hidden(A3); tag(A3,boxStory);
point3(C3,(3,3,-3),0.15); color(C3,gold); hidden(C3); tag(C3,boxStory);
point3(B3,(3,3,3),0.18); color(B3,lime); hidden(B3); tag(B3,boxStory);
line3(floorDiagonal3,(-3,-3,-3),(3,3,-3)); color(floorDiagonal3,gold); thick(floorDiagonal3,0.075); untraced(floorDiagonal3); hidden(floorDiagonal3); tag(floorDiagonal3,boxStory);
line3(height3,(3,3,-3),(3,3,3)); color(height3,magenta); thick(height3,0.075); untraced(height3); hidden(height3); tag(height3,boxStory);
line3(spaceDiagonal,(-3,-3,-3),(3,3,3)); color(spaceDiagonal,lime); thick(spaceDiagonal,0.105); untraced(spaceDiagonal); hidden(spaceDiagonal); tag(spaceDiagonal,boxStory);
text(a3,(0,0),"A"); size(a3,24*u); bold(a3); hidden(a3); pin3(a3,A3,(-18,16)); tag(a3,boxStory);
text(c3,(0,0),"C"); size(c3,24*u); bold(c3); color(c3,gold); hidden(c3); pin3(c3,C3,(16,14)); tag(c3,boxStory);
text(b3,(0,0),"B"); size(b3,24*u); bold(b3); color(b3,lime); hidden(b3); pin3(b3,B3,(16,-14)); tag(b3,boxStory);
point3(floorMid,(0,0,-3),0.01); hidden(floorMid);
point3(heightMid,(3,3,0),0.01); hidden(heightMid);
point3(spaceMid,(0,0,0),0.01); hidden(spaceMid);
text(floorLabel,(0,0),"AC = 5"); size(floorLabel,21*u); bold(floorLabel); color(floorLabel,gold); hidden(floorLabel); pin3(floorLabel,floorMid,(10,18)); tag(floorLabel,boxStory);
text(heightLabel,(0,0),"CB = 12"); size(heightLabel,21*u); bold(heightLabel); color(heightLabel,magenta); hidden(heightLabel); pin3(heightLabel,heightMid,(18,0)); tag(heightLabel,boxStory);
text(spaceLabel,(0,0),"AB = 13"); size(spaceLabel,22*u); bold(spaceLabel); color(spaceLabel,lime); hidden(spaceLabel); pin3(spaceLabel,spaceMid,(-18,-22)); tag(spaceLabel,boxStory);
// ---------------------------------------------------------------------------
// THE SOLUTION
// ---------------------------------------------------------------------------
step("pose the box problem") {
seq {
par { show(kicker,0.35); show(headline,0.50); show(chapter,0.40); show(caption,0.45); show(note,0.40); show(work,0.40); }
view3(boxFrame,"isometric",0.80,smooth,1.35);
show(boxFrame,0.75);
stagger(0.10) { show(A3,0.25); show(a3,0.25); show(B3,0.25); show(b3,0.25); }
par { show(spaceDiagonal,0.15); draw(spaceDiagonal,1.20,smooth); }
pulse(spaceDiagonal,0.65);
wait(0.60);
}
}
step("find the hidden floor diagonal") {
seq {
par {
fade(boxFrame,0.50);
fade(boxStory,0.50);
say(chapter,"1 · SOLVE THE 2D FLOOR",0.40,smooth);
say(caption,"The floor is a 3 × 4 rectangle. Its diagonal is the first hypotenuse.",0.50,smooth);
say(note,"This part is planar—so 2D is the clearest explanation.",0.45,smooth);
rewrite(work,`AC^2=3^2+4^2`,0.65,smooth);
}
stagger(0.10) {
show(floorBottom,0.10); draw(floorBottom,0.55,smooth);
show(floorRight,0.10); draw(floorRight,0.45,smooth);
show(floorTop,0.10); draw(floorTop,0.55,smooth);
show(floorLeft,0.10); draw(floorLeft,0.45,smooth);
show(A2,0.20); show(a2,0.20); show(C2,0.20); show(c2,0.20);
show(side4,0.25); show(side3,0.25);
}
par {
show(floorDiagonal2,0.10);
draw(floorDiagonal2,1.10,smooth);
rewrite(work,`AC=\sqrt{3^2+4^2}=\sqrt{25}=\textcolor{gold}{5}`,1.15,smooth);
}
show(base5,0.30);
pulse(floorDiagonal2,0.65);
wait(0.65);
}
}
step("use that answer inside the box") {
seq {
par {
fade(floor2d,0.50);
say(chapter,"2 · BUILD THE 3D RIGHT TRIANGLE",0.40,smooth);
say(caption,"Now AC = 5 meets the 12-unit height at a right angle.",0.50,smooth);
say(note,"The same coordinates return as a clean textbook projection.",0.45,smooth);
rewrite(work,`AB^2=AC^2+CB^2`,0.70,smooth);
}
view3(boxFrame,"isometric",0.75,smooth,1.35);
show(boxFrame,0.70);
stagger(0.10) {
show(A3,0.20); show(a3,0.20);
show(C3,0.20); show(c3,0.20);
show(floorDiagonal3,0.10); draw(floorDiagonal3,0.90,smooth); show(floorLabel,0.25);
show(height3,0.10); draw(height3,0.90,smooth); show(heightLabel,0.25);
show(B3,0.20); show(b3,0.20);
}
wait(0.35);
}
}
step("solve the space diagonal") {
seq {
par {
show(spaceDiagonal,0.10);
draw(spaceDiagonal,1.25,smooth);
rewrite(work,`AB=\sqrt{\textcolor{gold}{5^2}+\textcolor{magenta}{12^2}}`,0.95,smooth);
say(caption,"Pythagoras works again—this time across the volume.",0.45,smooth);
}
rewrite(work,`AB=\sqrt{25+144}=\sqrt{169}`,0.85,smooth);
par {
rewrite(work,`\boxed{AB=\textcolor{lime}{13}}`,0.75,smooth);
show(spaceLabel,0.30);
pulse(spaceDiagonal,0.80);
say(note,"Two right triangles · one exact answer",0.40,smooth);
}
wait(0.80);
}
}
step("see why the answer is spatial") {
seq {
par {
present3(boxFrame,spatial,0.70,smooth);
say(chapter,"3 · REVEAL THE SPATIAL TRUTH",0.40,smooth);
say(caption,"Orbit the same construction: AB crosses all three dimensions.",0.50,smooth);
say(note,"2D to calculate · textbook 3D to explain · spatial 3D to understand",0.50,smooth);
}
par { orbit3(48,26,12,1.80,smooth); pulse(spaceDiagonal,0.70); }
par { orbit3(132,34,12,1.80,smooth); pulse(boxFrame.axes,0.70); }
wait(0.85);
}
}
step("creator call to action") {
par {
fade(boxFrame,0.45);
fade(boxStory,0.45);
fade(work,0.35);
fade(chapter,0.35);
fade(caption,0.35);
fade(note,0.35);
show(me.endcard,0.60);
}
wait(1.80);
}
creator-point-to-plane-distance
A pure-3D vector-geometry Short: read the normal of x+y+z=3, project P=(2,2,5) onto the plane, construct the spatial right angle, derive the exact distance 2√3, and orbit the persistent point-plane proof.
// A pure-3D textbook problem. Every geometric object stays spatial:
// a point, a plane, its normal, the perpendicular foot, and the shortest path.
title("Shortest Distance from a Point to a Plane");
canvas("9:16");
template("neon");
creator(me,"@anish2good name=Manic_Geometry tagline=3D_math_made_visible yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=cyan secondary=magenta footer=compact safe=clean");
socials(me);
watermark(manicMark,(w*0.17,h*0.065),"Made With Manic");
endcard(me,"title=Make_3D_Math_Move cta=8gwifi.org/manic");
let u = (w+h-abs(w-h))/2160;
text(kicker,(cx,h*0.075),"MANIC · VECTOR GEOMETRY"); size(kicker,22*u); bold(kicker); color(kicker,cyan); hidden(kicker);
text(headline,(cx,h*0.125),"How far is a point from a plane?"); size(headline,38*u); bold(headline); wrap(headline,w*0.82); hidden(headline);
text(chapter,(cx,h*0.185),"THE SHORTEST PATH MUST BE PERPENDICULAR"); size(chapter,22*u); bold(chapter); color(chapter,gold); wrap(chapter,w*0.82); hidden(chapter);
equation(problem,(cx,h*0.235),`P=(2,2,5),\qquad \Pi:x+y+z=3`,36*u); hidden(problem);
equation(work,(cx,h*0.745),`d(P,\Pi)=?`,43*u); hidden(work);
text(caption,(cx,h*0.815),"A slanted route is longer. The normal reveals the minimum."); size(caption,24*u); bold(caption); wrap(caption,w*0.80); hidden(caption);
text(note,(cx,h*0.86),"Find the direction · locate the foot · measure the segment"); size(note,21*u); color(note,dim); wrap(note,w*0.80); hidden(note);
// The frame uses the same physical coordinates as the problem:
// x,y in [-3,3] and z in [0,6] map one-to-one into a 6-unit display cube.
// A perspective three-quarter view balances both facts: the plane reads as a
// full surface, while its perpendicular keeps enough screen length to remain
// obvious. This story never switches to an orthographic/textbook projection.
camera3((20,-5,17),(0,0,2.8),42);
frame3(world,(0,0,3),(6,6,6),"x=-3..3 y=-3..3 z=0..6 xmajor=1 ymajor=1 zmajor=2 planes=xy:min mode=spatial");
hidden(world);
// Pi: x+y+z=3, rendered as z=3-x-y over a bounded patch.
surface3(plane,"3-x-y",(-1.5,1.5),(-1.5,1.5),20);
color(plane,cyan);
finish3(plane,"material=glass shading=flat mesh=0.38 depth=0.32 shadow=0.12");
hidden(plane);
// Crisp boundary keeps the translucent plane readable on a phone.
line3(edge0,(-1.5,-1.5,6),(1.5,-1.5,3)); color(edge0,cyan); thick(edge0,0.045); untraced(edge0); hidden(edge0); tag(edge0,planeEdge);
line3(edge1,(1.5,-1.5,3),(1.5,1.5,0)); color(edge1,cyan); thick(edge1,0.045); untraced(edge1); hidden(edge1); tag(edge1,planeEdge);
line3(edge2,(1.5,1.5,0),(-1.5,1.5,3)); color(edge2,cyan); thick(edge2,0.045); untraced(edge2); hidden(edge2); tag(edge2,planeEdge);
line3(edge3,(-1.5,1.5,3),(-1.5,-1.5,6)); color(edge3,cyan); thick(edge3,0.045); untraced(edge3); hidden(edge3); tag(edge3,planeEdge);
// P is the given point. Q is its perpendicular projection onto Pi.
point3(P,(2,2,5),0.20); color(P,magenta); hidden(P); tag(P,geometry);
point3(Q,(0,0,3),0.18); color(Q,gold); hidden(Q); tag(Q,geometry);
text(pLabel,(0,0),"P (2, 2, 5)"); size(pLabel,22*u); bold(pLabel); color(pLabel,magenta); hidden(pLabel); pin3(pLabel,P,(20,-18)); tag(pLabel,geometry);
text(qLabel,(0,0),"Q (0, 0, 3)"); size(qLabel,22*u); bold(qLabel); color(qLabel,gold); hidden(qLabel); pin3(qLabel,Q,(-24,20)); tag(qLabel,geometry);
// The gradient of x+y+z-3 is n=(1,1,1).
arrow3(normal,(0,0,3),(1.15,1.15,4.15)); color(normal,gold); thick(normal,0.075); untraced(normal); hidden(normal);
point3(nTip,(1.15,1.15,4.15),0.01); hidden(nTip);
text(nLabel,(0,0),"n = (1, 1, 1)"); size(nLabel,21*u); bold(nLabel); color(nLabel,gold); hidden(nLabel); pin3(nLabel,nTip,(18,-14));
// The minimum-distance segment is parallel to n and ends at Q.
line3(distanceLine,(0,0,3),(2,2,5)); color(distanceLine,lime); thick(distanceLine,0.105); untraced(distanceLine); hidden(distanceLine); tag(distanceLine,geometry);
point3(distanceMid,(1,1,4),0.01); hidden(distanceMid);
text(distanceLabel,(0,0),"d = 2√3"); size(distanceLabel,23*u); bold(distanceLabel); color(distanceLabel,lime); hidden(distanceLabel); pin3(distanceLabel,distanceMid,(-22,-20)); tag(distanceLabel,geometry);
// A small spatial right-angle mark: one side lies in Pi, one follows n.
line3(right0,(0,0,3),(0.45,-0.45,3)); color(right0,fg); thick(right0,0.045); untraced(right0); hidden(right0); tag(right0,rightMark);
line3(right1,(0.45,-0.45,3),(0.70,-0.20,3.25)); color(right1,fg); thick(right1,0.045); untraced(right1); hidden(right1); tag(right1,rightMark);
line3(right2,(0.70,-0.20,3.25),(0.25,0.25,3.25)); color(right2,fg); thick(right2,0.045); untraced(right2); hidden(right2); tag(right2,rightMark);
step("pose the spatial problem") {
seq {
par {
show(kicker,0.35); show(headline,0.50); show(chapter,0.45);
show(problem,0.55); show(work,0.40); show(caption,0.45); show(note,0.40);
}
show(world,0.70);
par {
show(plane,0.75);
show(edge0,0.10); draw(edge0,0.70,smooth);
show(edge1,0.10); draw(edge1,0.70,smooth);
show(edge2,0.10); draw(edge2,0.70,smooth);
show(edge3,0.10); draw(edge3,0.70,smooth);
}
stagger(0.12) { show(P,0.25); show(pLabel,0.30); pulse(P,0.60); }
wait(0.65);
}
}
step("read the plane normal") {
seq {
par {
say(chapter,"1 · READ THE NORMAL FROM THE PLANE",0.40,smooth);
say(caption,"The coefficients of x, y, and z point perpendicular to Π.",0.50,smooth);
say(note,"For ax + by + cz + d = 0, the normal is (a,b,c).",0.45,smooth);
rewrite(work,`\Pi:x+y+z-3=0`,0.65,smooth);
fade(pLabel,0.30);
}
par {
show(Q,0.25);
show(normal,0.10); draw(normal,1.10,smooth);
show(nLabel,0.35);
rewrite(work,`\mathbf n=(1,1,1)`,0.80,smooth);
}
pulse(normal,0.65);
wait(0.60);
}
}
step("locate the perpendicular foot") {
seq {
par {
fade(normal,0.35); fade(nLabel,0.35);
say(chapter,"2 · DROP THE PERPENDICULAR",0.40,smooth);
say(caption,"Move from P opposite the normal until the plane equation becomes true.",0.50,smooth);
say(note,"Q = P − t n must lie on Π.",0.40,smooth);
rewrite(work,`Q=(2,2,5)-t(1,1,1)`,0.80,smooth);
}
rewrite(work,`(2-t)+(2-t)+(5-t)=3\Rightarrow t=2`,1.00,smooth);
par {
show(distanceLine,0.10); draw(distanceLine,1.20,smooth);
show(right0,0.10); draw(right0,0.55,smooth);
show(right1,0.10); draw(right1,0.45,smooth);
show(right2,0.10); draw(right2,0.55,smooth);
rewrite(work,`Q=(0,0,3)`,0.75,smooth);
}
pulse(Q,0.65);
wait(0.65);
}
}
step("measure the shortest segment") {
seq {
par {
say(chapter,"3 · MEASURE THE NORMAL SEGMENT",0.40,smooth);
say(caption,"The point-to-plane formula measures exactly the green segment PQ.",0.50,smooth);
say(note,"Absolute value gives distance; the denominator normalizes n.",0.45,smooth);
rewrite(work,`d=\frac{|2+2+5-3|}{\sqrt{1^2+1^2+1^2}}`,1.05,smooth);
}
rewrite(work,`d=\frac{6}{\sqrt3}=\textcolor{lime}{2\sqrt3}`,0.90,smooth);
par {
rewrite(work,`\boxed{d(P,\Pi)=\textcolor{lime}{2\sqrt3}}`,0.75,smooth);
show(distanceLabel,0.30);
pulse(distanceLine,0.80);
}
wait(0.75);
}
}
step("orbit the proof") {
seq {
par {
say(chapter,"THE ANSWER IS A SPATIAL RELATIONSHIP",0.45,smooth);
say(caption,"Orbit the proof: PQ stays normal to Π from every viewpoint.",0.50,smooth);
say(note,"One plane · one projection · one shortest path",0.45,smooth);
fade(pLabel,0.35); fade(qLabel,0.35);
}
par { orbit3(38,28,24,1.80,smooth); pulse(distanceLine,0.70); }
par { orbit3(68,30,24,1.80,smooth); pulse(plane,0.70); }
wait(0.80);
}
}
step("creator call to action") {
par {
fade(world,0.45); fade(plane,0.45); fade(planeEdge,0.45);
fade(geometry,0.45); fade(rightMark,0.45);
fade(problem,0.35); fade(work,0.35); fade(chapter,0.35);
fade(caption,0.35); fade(note,0.35);
show(me.endcard,0.60);
}
wait(1.80);
}
frame3-grid-policies
The six Asymptote-inspired scientific grid policies in one review: lower walls, a logarithmic axis, top and explicit planes, coloured parallel sections, major/minor grids, and independent per-axis intervals.
// Review file for the six Asymptote grid3 policies that motivated frame3.
// Each chapter is a normal frame3 declaration, not a renderer special case.
title("Scientific Grid Planes — Asymptote Parity");
canvas("16:9");
template("mono");
watermark(manicMark,(170,70),"Made With Manic");
text(titleText,(640,68),"One frame vocabulary · six grid policies"); size(titleText,30); bold(titleText);
text(caption,(640,670),"Three lower walls"); size(caption,24); bold(caption); color(caption,gold);
camera3((10,-12,8),(0,0,0),15,orthographic);
frame3(walls,(0,0,0),(8,8,6),"x=-2..2 y=-2..2 z=-1..3 planes=xy:min,xz:min,yz:min major=1 mode=textbook");
frame3(logWalls,(0,0,0),(8,8,6),"x=0..4 y=0..4 z=0.1..100 planes=xy:min,xz:min zscale=log major=1 mode=textbook");
frame3(positioned,(0,0,0),(8,8,6),"x=-2..2 y=-2..2 z=-2..2 planes=xy:max,xz:0 major=1 mode=textbook");
frame3(parallel,(0,0,0),(8,8,6),"x=-2..2 y=-2..2 z=-2..2 planes=xy:-0.5@cyan,xy:1.5@magenta major=1 mode=textbook");
frame3(majorMinor,(0,0,0),(8,8,6),"x=-2..2 y=-2..2 z=-1..2 planes=xy:min major=1 minor=0.5 mode=textbook");
frame3(axisPolicy,(0,0,0),(8,8,6),"x=-2..2 y=-2..2 z=-2..2 planes=xy:min,yz:min xmajor=0.5 ymajor=1 zmajor=2 mode=textbook");
hidden(logWalls); hidden(positioned); hidden(parallel); hidden(majorMinor); hidden(axisPolicy);
step("three lower walls") {
show(walls,0.65);
wait(0.70);
}
step("linear floor with logarithmic height") {
par { fade(walls,0.45); show(logWalls,0.60); say(caption,"Linear floor · logarithmic z",0.40,smooth); }
wait(0.70);
}
step("top and explicit-position planes") {
par { fade(logWalls,0.45); show(positioned,0.60); say(caption,"XY at max · XZ at y=0",0.40,smooth); }
wait(0.70);
}
step("parallel colored sections") {
par { fade(positioned,0.45); show(parallel,0.60); say(caption,"Two parallel XY sections",0.40,smooth); }
wait(0.70);
}
step("major and minor lines") {
par { fade(parallel,0.45); show(majorMinor,0.60); say(caption,"Major ticks derive the major grid",0.40,smooth); }
wait(0.70);
}
step("independent axis policy") {
par { fade(majorMinor,0.45); show(axisPolicy,0.60); say(caption,"Each axis chooses its own interval",0.40,smooth); }
present3(axisPolicy,spatial,0.70,smooth);
orbit3(118,30,12,2.10,smooth);
wait(1.40);
}
asymptote-randomwalk3-reference
The recurring Asymptote 3D random-walk families reduced to two readable models: world-axis choices and a local turtle frame with 90°/60° turns.
// The seven Asymptote randomwalk examples reduce to two reusable models:
// six world-axis choices, or a local 3D turtle that turns by 90°/60°.
title("Deterministic 3D Random Walks");
canvas("16:9");
template("mono");
watermark(mark, (w*0.11,h*0.075), "Made With Manic");
text(kicker, (cx,h*0.07), "GENERATIVE 3D · RANDOMWALK3");
text(headline, (cx,h*0.13), "The seed fixes the journey. The model changes its shape.");
text(caption, (cx,h*0.91), "Axis walk · uniform choices · direction colour · camera-aware depth");
size(kicker,19); bold(kicker); color(kicker,dim);
size(headline,32); bold(headline);
size(caption,20); bold(caption); color(caption,gold);
camera3((12,-15,10),(0,0,0),38,orthographic);
randomwalk3(axisWalk, (0,0,0), 12000, 21,
"mode=axis distribution=uniform color=direction shade=depth scale=0.12");
untraced(axisWalk);
randomwalk3(turtle90, (0,0,0), 9000, 21,
"mode=turtle angle=90 distribution=gaussian color=turn shade=depth scale=0.12");
untraced(turtle90); hidden(turtle90);
randomwalk3(turtle60, (0,0,0), 9000, 21,
"mode=turtle angle=60 distribution=uniform color=turn shade=depth scale=0.12");
untraced(turtle60); hidden(turtle60);
step("six world directions") {
par { draw(axisWalk,2.20,smooth); view3(axisWalk,"fit",0.60,smooth,1.20); }
}
wait(0.65);
step("the frame can turn with the walker") {
par {
fade(axisWalk,0.40);
show(turtle90,0.05);
say(caption,"Turtle frame · Gaussian choices · 90° turns",0.40);
}
par { draw(turtle90,2.20,smooth); view3(turtle90,"fit",0.60,smooth,1.20); }
}
wait(0.65);
step("change one angle") {
par {
fade(turtle90,0.40);
show(turtle60,0.05);
say(caption,"Turtle frame · uniform choices · 60° turns",0.40);
}
par { draw(turtle60,2.20,smooth); view3(turtle60,"fit",0.60,smooth,1.20); }
}
wait(0.70);
step("inspect the structure") {
orbit3(115,28,13,2.10,smooth);
}
wait(1.20);
creator-randomwalk3-diffusion
A creator Short grows the same seeded 3D walk from 100 to 10,000 decisions and reveals the large-scale shape hiding inside random local choices.
// Creator short: many random decisions do not create visual noise only—the
// choice model leaves a characteristic 3D signature.
title("Can Randomness Have a Shape?");
canvas("9:16");
template("paper");
creator(me, "@anish2good name=Manic_Generative tagline=Randomness_made_visible yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=cyan secondary=magenta footer=compact cta=Animate_the_invisible safe=clean");
socials(me);
watermark(mark, (w*0.16,h*0.055), "Made With Manic");
endcard(me, "title=Make_Invisible_Systems_Visible cta=8gwifi.org/manic");
let u = (w+h-abs(w-h))/1080;
text(kicker, (cx,h*0.11), "MANIC · RANDOM SYSTEMS");
text(headline, (cx,h*0.18), "Can randomness have a shape?");
text(caption, (cx,h*0.77), "Start at one point. Choose one of six directions.");
text(count, (cx,h*0.70), "100 STEPS");
size(kicker,20*u); bold(kicker); color(kicker,cyan);
size(headline,31*u); bold(headline); wrap(headline,w*0.78);
size(caption,23*u); bold(caption); wrap(caption,w*0.75);
size(count,20*u); bold(count); color(count,gold);
camera3((9,-12,8),(0,0,0),38,orthographic);
randomwalk3(w100,(0,0,0),100,42,"mode=axis color=direction scale=0.25");
randomwalk3(w1000,(0,0,0),1000,42,"mode=axis color=direction shade=depth scale=0.16");
randomwalk3(w10000,(0,0,0),10000,42,"mode=axis color=direction shade=depth scale=0.09");
untraced(w100); untraced(w1000); untraced(w10000);
hidden(w1000); hidden(w10000);
hidden(kicker); hidden(headline); hidden(caption); hidden(count);
step("one random journey") {
par {
show(kicker,0.35); show(headline,0.45); show(caption,0.40); show(count,0.35);
draw(w100,1.25,smooth); view3(w100,"fit",0.55,smooth,1.35);
}
}
wait(0.55);
step("the cloud begins to emerge") {
par {
fade(w100,0.35); show(w1000,0.05);
say(count,"1,000 STEPS",0.30);
say(caption,"More choices reveal a spreading 3D cloud.",0.40);
}
par { draw(w1000,1.75,smooth); view3(w1000,"fit",0.55,smooth,1.35); }
}
wait(0.60);
step("scale exposes the law") {
par {
fade(w1000,0.35); show(w10000,0.05);
say(count,"10,000 STEPS · SAME SEED",0.30);
say(caption,"The individual turns are random. The large-scale diffusion is not.",0.45);
}
par { draw(w10000,2.30,smooth); view3(w10000,"fit",0.60,smooth,1.35); }
orbit3(112,30,11,1.70,smooth);
}
wait(0.85);
step("creator takeaway") {
say(caption,"Manic turns a model and a seed into a camera-ready explanation.",0.45);
recolor(headline,gold,0.45);
}
wait(1.00);
step("call to action") {
par {
fade(kicker,0.30); fade(headline,0.30); fade(caption,0.30); fade(count,0.30);
fade(w10000,0.40); fade(me.footer,0.30); show(me.endcard,0.60);
}
}
wait(1.80);
parameterized-generated-families3
The compact generated-family reference: one ordinary parameter continuously changes a helix, height field, and torus while every object keeps its id, sample topology, material, and timeline identity.
// PARAMETERIZED GENERATED FAMILIES — one ordinary parameter, three stable 3-D
// objects. `p` changes sampled points; ids, materials, transforms, and sample
// topology stay intact. No per-vertex scripting or snapshot replacement.
title("Parameterized Generated Families");
canvas("16:9");
template("mono");
watermark(mark, (170, 55), "Made With Manic");
text(kicker, (640, 48), "MANIC · GENERATED 3-D FAMILIES");
text(headline, (640, 92), "One parameter · three continuous shapes");
text(caption, (640, 664), "curve3(t,p) · surface3(x,y,p) · param3(u,v,p)");
size(kicker, 18); color(kicker, dim); bold(kicker);
size(headline, 34); bold(headline);
size(caption, 19); color(caption, dim);
parameter(shape, (640, 612), 0, 0, 1, "shape", 2);
if h > 1.45*w {
camera3((0, -36, 8), (0, 0, 0.4), 40);
}
else {
camera3((10, -17, 9), (0, 0, 0.4), 40);
}
grid3(floor, (0, 0, -2.2), 8, 1);
color(floor, dim); opacity(floor, 0.14);
// A widening helix at the left.
curve3(helix,
"-4 + cos(t)",
"sin(t)",
"-1.7 + 0.55*t",
(0, 6.2832));
bind(shape, helix, formula,
"-4 + (1+0.45*p)*cos(t)",
"(1+0.45*p)*sin(t)",
"-1.7 + 0.55*t");
color(helix, cyan); thick(helix, 0.065); untraced(helix);
tag(helix, family);
// A bowl that becomes a rippling field in the middle.
surface3(land, "0.15*(x*x+y*y)", (-1.7,1.7), (-1.7,1.7), 28);
bind(shape, land, formula,
"(1-p)*0.15*(x*x+y*y) + p*0.72*sin(1.7*x)*cos(1.7*y)");
color(land, gold);
finish3(land, "material=glass shading=smooth mesh=0.30 depth=0.28 shadow=0.12");
tag(land, family);
// A torus whose tube inflates at the right.
param3(ring,
"4+(1.15+0.20*cos(v))*cos(u)",
"(1.15+0.20*cos(v))*sin(u)",
"0.20*sin(v)",
(0,6.2832), (0,6.2832), 28);
bind(shape, ring, formula,
"4+(1.15+(0.20+0.52*p)*cos(v))*cos(u)",
"(1.15+(0.20+0.52*p)*cos(v))*sin(u)",
"(0.20+0.52*p)*sin(v)");
color(ring, magenta);
finish3(ring, "material=metal shading=smooth mesh=0.22 depth=0.30 shadow=0.16");
tag(ring, family);
hidden(helix); hidden(land); hidden(ring);
step("reveal one stable family") {
par {
view3(family, "fit", 0.9, smooth, 1.55);
stagger(0.16) {
show(helix, 0.45);
show(land, 0.45);
show(ring, 0.45);
}
}
}
wait(0.45);
step("change the shared parameter") {
par {
to(shape, value, 1, 3.4, smooth);
say(caption, "The same three objects are resampled smoothly from p = 0 to p = 1.", 0.45);
}
}
wait(1.2);
creator-bowl-to-saddle
A vertical calculus Short asks when a bowl loses one direction of curvature. One persistent surface passes through the exact p=1/2 threshold and becomes a saddle, then closes with a creator CTA.
// CREATOR SHORT — a real calculus problem told through one continuously
// deforming surface. The shape is not replaced between the bowl, threshold,
// and saddle states.
title("When Does a Bowl Become a Saddle?");
canvas("9:16");
template("paper");
creator(me, "@anish2good name=Manic_Math tagline=Equations_that_move yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=cyan secondary=magenta footer=signature cta=Create_your_own safe=reels");
socials(me);
watermark(mark, (w*0.955-100, h*0.045+24), "Made With Manic");
text(kicker, (540, 164), "A 20-SECOND CALCULUS STORY");
text(headline, (540, 226), "When does a bowl stop being a bowl?");
text(problem, (540, 292), "Find the exact value of p where one curvature disappears.");
size(kicker, 21); bold(kicker); color(kicker, cyan); hidden(kicker);
size(headline, 38); bold(headline); wrap(headline, 880); hidden(headline);
size(problem, 22); color(problem, dim); wrap(problem, 840); hidden(problem);
equation(rule, (540, 378), `z=0.22x^2+0.22(1-2p)y^2`, 39);
hidden(rule);
parameter(mix, (540, 1270), 0, 0, 1, "p", 2);
camera3((8.5, -11.5, 7.5), (0, 0, 0), 41);
surface3(world, "0.22*(x*x+y*y)", (-3,3), (-3,3), 34);
bind(mix, world, formula, "0.22*x*x + 0.22*(1-2*p)*y*y");
color(world, cyan);
finish3(world, "material=glass shading=smooth mesh=0.34 depth=0.34 shadow=0.16");
hidden(world);
text(chapter, (540, 1352), "p = 0 · positive curvature in both directions");
text(insight, (540, 1422), "Watch the coefficient of y².");
text(cta, (540, 1398), "Build the transformation, not the keyframes.");
text(link, (540, 1452), "TRY MANIC → 8gwifi.org/manic");
size(chapter, 23); bold(chapter); color(chapter, fg); wrap(chapter, 850); hidden(chapter);
size(insight, 21); color(insight, dim); hidden(insight);
size(cta, 23); bold(cta); hidden(cta);
size(link, 20); bold(link); color(link, cyan); hidden(link);
step("pose the problem") {
par {
show(kicker, 0.35);
show(headline, 0.50);
show(problem, 0.45);
show(rule, 0.55);
show(world, 0.65);
show(chapter, 0.40);
show(insight, 0.40);
view3(world, "isometric", 0.9, smooth, 1.62);
}
}
wait(0.65);
step("reach the turning point") {
par {
to(mix, value, 0.5, 2.8, smooth);
say(chapter, "p = ½ · the y² coefficient becomes zero", 0.45);
say(insight, "At this instant the bowl becomes a one-directional trough.", 0.45);
rewrite(rule, `p=\frac12\quad\Longrightarrow\quad z=0.22x^2`, 1.10, smooth);
orbit3(-45, 28, 16.2, 2.8, smooth);
}
}
wait(0.75);
step("cross into a saddle") {
par {
to(mix, value, 1, 2.8, smooth);
say(chapter, "p > ½ · one curvature turns negative", 0.45);
say(insight, "The same persistent surface is now a saddle.", 0.45);
rewrite(rule, `p=1\quad\Longrightarrow\quad z=0.22(x^2-y^2)`, 1.10, smooth);
orbit3(-62, 25, 16.2, 2.8, smooth);
}
}
wait(0.65);
step("answer and invite") {
par {
say(problem, "Answer: p = ½ is the exact transition.", 0.50);
fade(chapter, 0.35);
fade(insight, 0.35);
show(cta, 0.45);
show(link, 0.45);
}
}
wait(1.8);
story-living-dependency-cloud
A living dependency cloud grows, links itself by nearest neighbours, and drifts as one batched 3D cast. The relationships stay attached without hand-authoring dozens of nodes or links.
title("The Dependency Cloud That Refuses to Freeze");
canvas("9:16");
template("paper");
watermark(mark, (w*0.16, h*0.055), "Made With Manic");
text(kicker, (cx, h*0.085), "ENGINE FOUNDATION · BATCHED RELATIONSHIPS");
text(headline, (cx, h*0.13), "Can 180 dependencies move as one idea?");
text(caption, (cx, h*0.82), "One collection. Stable children. Every relationship stays truthful.");
size(kicker, 20); color(kicker, dim); bold(kicker); hidden(kicker);
size(headline, 34); bold(headline); wrap(headline, w*0.82); hidden(headline);
size(caption, 22); color(caption, dim); wrap(caption, w*0.78); hidden(caption);
camera3((14,-20,13),(0,0,0),46);
collection3(services,(0,0,0),180,(4.2,2.6,2.2),41,0.055);
links3(dependencies,services,nearest,2);
color(services,cyan);
color(dependencies,dim);
hidden(services);
hidden(dependencies);
step("the-problem") {
show(kicker,0.35);
show(headline,0.45);
show(caption,0.40);
}
wait(0.45);
step("the-system-awakens") {
par {
show(services,0.65);
show(dependencies,0.85);
say(caption,"The engine batches the repeated geometry but preserves every indexed identity.",0.45);
}
}
wait(0.35);
step("relationships-remain-live") {
par {
drift3(services,6.0,0.42);
orbit3(35,24,28.0,6.0,smooth);
say(caption,"As the nodes drift, the nearest-neighbour graph is derived again from the same state.",0.45);
}
}
wait(0.45);
step("takeaway") {
pulse(services,0.7);
say(caption,"Large dependency scenes become one creator intention—not hundreds of scripts.",0.45);
}
wait(1.2);
story-dependent-chain-history
An articulated signal arm is generated as one dependent chain: every endpoint begins at the previous endpoint, while the tip leaves a truthful history of its real route.
title("How Rotations Become a Drawing");
canvas("9:16");
template("blank");
watermark(mark, (w*0.16, h*0.055), "Made With Manic");
text(kicker, (cx, h*0.085), "ENGINE FOUNDATION · DEPENDENT CHAINS");
text(headline, (cx, h*0.13), "A drawing can hide inside rotating links");
text(caption, (cx, h*0.83), "Each endpoint begins where the previous endpoint finishes.");
size(kicker,20); color(kicker,dim); bold(kicker); hidden(kicker);
size(headline,34); bold(headline); wrap(headline,w*0.84); hidden(headline);
size(caption,22); color(caption,dim); wrap(caption,w*0.80); hidden(caption);
camera3((0,-18,13),(0,0,0),46);
collection3(harmonics,(0,0,0),7,(0,0,0),7,0.10);
links3(arms,harmonics,chain);
trail3(memory,harmonics,6,0.035);
child3(tip,harmonics,6,0.14);
color(harmonics,cyan);
color(arms,magenta);
color(memory,gold);
color(tip,gold);
hidden(harmonics); hidden(arms); hidden(memory); hidden(tip);
step("question") {
show(kicker,0.35);
show(headline,0.45);
show(caption,0.40);
}
wait(0.35);
step("build-the-chain") {
par {
show(harmonics,0.55);
show(arms,0.65);
show(memory,0.45);
show(tip,0.45);
cue(tick);
say(caption,"Seven stable links share one generated dependency chain.",0.40);
}
}
wait(0.30);
step("remember-the-tip") {
par {
chain3(harmonics,"2.2 1.1 0.72 0.52 0.40 0.32 0.26","1 -3 5 -7 9 -11 13",8.0);
say(caption,"Only the moving tip writes history; the rest remain a readable mechanism.",0.45);
}
}
wait(0.45);
step("takeaway") {
cue(chime);
pulse(tip,0.7);
say(caption,"The same foundation serves epicycles, linkages, robot arms, and live derived plots.",0.45);
}
wait(1.2);
fourier-series-live-wave
Twelve visible orbit rings form one epicycle chain while historyplot3 turns every
endpoint’s exact y-history into a coloured partial sum across two complete periods—one
continuous motion, one source of truth.
// A focused Fourier story: every orbit and every partial sum is generated from
// the same live collection. The circles move; their histories become the wave.
title("A Wave Drawn by Rotating Circles");
canvas("9:16");
template("blank");
watermark(mark,(w*0.15,h*0.045),"Made With Manic");
text(kicker,(cx,h*0.075),"FOURIER SERIES · LIVE DERIVED HISTORY");
text(headline,(cx,h*0.115),"Can rotating circles draw a wave?");
text(caption,(cx,h*0.18),"Every coloured trace is the real history of one moving endpoint.");
text(cta,(cx,h*0.925),"BUILD YOUR OWN VISUAL STORY → 8gwifi.org/manic");
size(kicker,18); color(kicker,dim); bold(kicker); hidden(kicker);
size(headline,31); bold(headline); wrap(headline,w*0.84); hidden(headline);
size(caption,20); color(caption,dim); wrap(caption,w*0.84); hidden(caption);
size(cta,21); color(cta,cyan); bold(cta); hidden(cta);
camera3((-29,-25,43),(1,-2,0),42,orthographic);
collection3(harmonics,(-3,5,0),12,(0,0,0),11,0.065);
line3(sweep,(-3,5,0),(10,5,0)); hidden(sweep);
links3(arms,harmonics,chain);
child3(tip,harmonics,11,0.13);
arrow3(chainX,(-11,5,0),(7,5,0));
arrow3(chainY,(-3,-2,0),(-3,10.5,0));
arrow3(plotX,(-7,-10.5,0),(12,-10.5,0));
arrow3(plotY,(-7,-10.5,0),(-7,-4.2,0));
point3(shotA,(-7.5,-11,0),0.01);
point3(shotB,(8.5,10.5,0),0.01);
tag(shotA,shot); tag(shotB,shot);
hidden(shotA); hidden(shotB);
tag(harmonics,construction); tag(arms,construction); tag(tip,construction);
tag(chainX,construction); tag(chainY,construction);
tag(plotX,construction); tag(plotY,construction);
for i in 0..12 {
ring3(orbit{i},harmonics,i,72);
historyplot3(partial{i},harmonics,i,y,(-7,-10.5,0),(19,6));
thick(orbit{i},0.030);
thick(partial{i},0.020);
tag(orbit{i},construction);
tag(partial{i},construction);
}
equation(formula,(0,0),`f(t)=\sum_{n=1}^{12}\frac{2(-1)^{n+1}}{\pi n}\sin(nt)`,25);
label3(formula,(-4,-2.7,0),0.82);
tag(formula,construction);
color(harmonics,fg); color(arms,fg); color(tip,fg);
color(chainX,fg); color(chainY,fg); color(plotX,fg); color(plotY,fg);
color(formula,fg);
thick(chainX,0.016); thick(chainY,0.016);
thick(plotX,0.016); thick(plotY,0.016);
color(orbit0,lime); color(partial0,lime);
color(orbit1,gold); color(partial1,gold);
color(orbit2,orange); color(partial2,orange);
color(orbit3,coral); color(partial3,coral);
color(orbit4,red); color(partial4,red);
color(orbit5,magenta); color(partial5,magenta);
color(orbit6,violet); color(partial6,violet);
color(orbit7,indigo); color(partial7,indigo);
color(orbit8,blue); color(partial8,blue);
color(orbit9,cyan); color(partial9,cyan);
color(orbit10,mint); color(partial10,mint);
color(orbit11,fg); color(partial11,fg);
hidden(construction);
step("ask the visual question") {
show(kicker,0.30);
show(headline,0.42);
show(caption,0.38);
}
wait(0.35);
step("reveal one connected machine") {
par {
show(construction,0.85);
view3(shot,"top",1.35,smooth,1.08);
say(caption,"Each circle starts where the previous circle ends.",0.38);
}
}
wait(0.30);
step("let motion become data") {
par {
// Doubling every angular rate makes the base harmonic complete exactly
// two revolutions while preserving all relative frequencies.
chain3(harmonics,"2.0372 -1.0186 0.6791 -0.5093 0.4074 -0.3395 0.2910 -0.2546 0.2264 -0.2037 0.1852 -0.1698","2 4 6 8 10 12 14 16 18 20 22 24",16.0);
travel3(harmonics,sweep,16.0,linear);
seq {
say(caption,"Cycle one: the broad motion appears, then smaller circles restore its detail.",0.40);
wait(6.10);
say(caption,"Cycle two: the same relationships repeat continuously—without a reset or jump.",0.40);
wait(5.20);
say(caption,"The endpoint histories now contain two complete periods of the signal.",0.40);
}
}
}
wait(0.35);
step("create with Manic") {
par {
pulse(tip,0.75);
pulse(partial11,0.75);
show(cta,0.45);
say(caption,"Describe the relationship. Manic keeps the circles, histories, motion, and camera together.",0.42);
}
}
wait(1.45);
definedmotion-fourier-gallery
Five depth-stacked Fourier families rotate at once while twenty coloured ring3
orbits drive twenty world-space partial histories per family. Focus and overview cameras
observe the same compiled motion before a creator CTA closes the Short.
// Visual acceptance story for DefinedMotion resources/animation2.gif.
// Twenty visible orbit rings create each relation. Every coloured partial-sum
// history is derived from the exact endpoint that draws it.
title("Fourier Series — Rotations Become Waves");
canvas("9:16");
template("mono");
watermark(mark,(w*0.15,h*0.045),"Made With Manic");
text(chapter,(cx,h*0.075),"FOURIER SERIES · FIVE RELATIONS");
text(caption,(cx,h*0.85),"Twenty rotating circles. One continuously derived signal.");
text(cta,(cx,h*0.925),"BUILD VISUAL EXPLANATIONS → 8gwifi.org/manic");
size(chapter,18); color(chapter,dim); bold(chapter); hidden(chapter);
size(caption,20); color(caption,dim); hidden(caption);
size(cta,21); color(cta,cyan); bold(cta); hidden(cta);
camera3((-31,-25,44),(1,-2,0),42,orthographic);
def relation(k,zv) {
// The chain, its two local axes, and the waveform axes all share one layer.
collection3(series{k},(-3,5,zv),20,(0,0,0),11,0.055);
line3(sweep{k},(-3,5,zv),(10,5,zv));
hidden(sweep{k});
links3(arms{k},series{k},chain);
child3(tip{k},series{k},19,0.12);
arrow3(chainX{k},(-11,5,zv),(7,5,zv));
arrow3(chainY{k},(-3,-3,zv),(-3,13,zv));
arrow3(plotX{k},(-7,-12,zv),(12,-12,zv));
arrow3(plotY{k},(-7,-12,zv),(-7,-3,zv));
// A cinematic crop: the axes may continue beyond the frame, as in the
// reference, while the active rings and the growing history stay dominant.
point3(shot{k}A,(-7.5,-10,zv),0.01);
point3(shot{k}B,(8.5,10,zv),0.01);
tag(shot{k}A,shot{k}); tag(shot{k}B,shot{k});
hidden(shot{k}A); hidden(shot{k}B);
tag(series{k},layer{k}); tag(arms{k},layer{k}); tag(tip{k},layer{k});
tag(chainX{k},layer{k}); tag(chainY{k},layer{k});
tag(plotX{k},layer{k}); tag(plotY{k},layer{k});
tag(layer{k},stack);
for i in 0..20 {
ring3(orbit{k}_{i},series{k},i,72);
historyplot3(wave{k}_{i},series{k},i,y,(-7,-12,zv),(19,8.5));
thick(orbit{k}_{i},0.022);
thick(wave{k}_{i},0.014);
tag(orbit{k}_{i},layer{k}); tag(wave{k}_{i},layer{k});
}
color(series{k},fg); color(arms{k},fg); color(tip{k},fg);
color(chainX{k},fg); color(chainY{k},fg);
color(plotX{k},fg); color(plotY{k},fg);
thick(chainX{k},0.016); thick(chainY{k},0.016);
thick(plotX{k},0.016); thick(plotY{k},0.016);
color(orbit{k}_0,lime); color(wave{k}_0,lime);
color(orbit{k}_1,gold); color(wave{k}_1,gold);
color(orbit{k}_2,gold); color(wave{k}_2,gold);
color(orbit{k}_3,magenta); color(wave{k}_3,magenta);
color(orbit{k}_4,coral); color(wave{k}_4,coral);
color(orbit{k}_5,red); color(wave{k}_5,red);
color(orbit{k}_6,orange); color(wave{k}_6,orange);
color(orbit{k}_7,magenta); color(wave{k}_7,magenta);
color(orbit{k}_8,violet); color(wave{k}_8,violet);
color(orbit{k}_9,indigo); color(wave{k}_9,indigo);
color(orbit{k}_10,cyan); color(wave{k}_10,cyan);
color(orbit{k}_11,blue); color(wave{k}_11,blue);
color(orbit{k}_12,mint); color(wave{k}_12,mint);
color(orbit{k}_13,lime); color(wave{k}_13,lime);
color(orbit{k}_14,gold); color(wave{k}_14,gold);
color(orbit{k}_15,cyan); color(wave{k}_15,cyan);
color(orbit{k}_16,lime); color(wave{k}_16,lime);
color(orbit{k}_17,magenta); color(wave{k}_17,magenta);
color(orbit{k}_18,coral); color(wave{k}_18,coral);
color(orbit{k}_19,cyan); color(wave{k}_19,cyan);
}
relation(0,-20);
relation(1,-10);
relation(2,0);
relation(3,10);
relation(4,20);
point3(overviewA,(-11,-12,-20),0.01);
point3(overviewB,(12,13,20),0.01);
tag(overviewA,overviewShot); tag(overviewB,overviewShot);
hidden(overviewA); hidden(overviewB);
equation(formula0,(0,0),`\sum_{n=1}^{N}\frac{4}{\pi(2n-1)}\sin((2n-1)t)`,26);
equation(formula1,(0,0),`\sum_{n=1}^{N}\frac{2(-1)^{n+1}}{\pi n}\sin(nt)`,26);
equation(formula2,(0,0),`\sum_{n=1}^{N}\frac{2w_n}{\pi n}\sin(nt)`,26);
equation(formula3,(0,0),`\sum_{n=1}^{N}\frac{2}{\pi n}\sin(n^2t)`,26);
equation(formula4,(0,0),`\sum_{n=1}^{N}\frac{2r_n}{\pi n}\sin(nt)`,26);
label3(formula0,(-8,-2,-20),0.82);
label3(formula1,(-8,-2,-10),0.82);
label3(formula2,(-8,-2,0),0.82);
label3(formula3,(-8,-2,10),0.82);
label3(formula4,(-8,-2,20),0.82);
for i in 0..5 {
tag(formula{i},layer{i}); tag(formula{i},stack); color(formula{i},fg);
}
hidden(stack);
step("enter the Fourier world") {
show(chapter,0.30);
show(caption,0.35);
show(stack,0.85);
}
wait(0.35);
step("twenty circles write five signals") {
par {
chain3(series0,"4.0744 1.3581 0.8149 0.5821 0.4527 0.3704 0.3134 0.2716 0.2397 0.2144 0.1940 0.1771 0.1630 0.1509 0.1405 0.1314 0.1235 0.1164 0.1101 0.1045","1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39",17.0);
chain3(series1,"2.0372 -1.0186 0.6791 -0.5093 0.4074 -0.3395 0.2910 -0.2546 0.2264 -0.2037 0.1852 -0.1698 0.1567 -0.1455 0.1358 -0.1273 0.1198 -0.1132 0.1072 -0.1019","1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20",17.0);
chain3(series2,"0 0.0276 0.0716 0.1154 0.1537 0.1838 0.2040 0.2136 0.2127 0.2023 0.1839 0.1595 0.1314 0.1020 0.0735 0.0480 0.0271 0.0119 0.0029 0","1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20",17.0);
chain3(series3,"2.0372 1.0186 0.6791 0.5093 0.4074 0.3395 0.2910 0.2546 0.2264 0.2037 0.1852 0.1698 0.1567 0.1455 0.1358 0.1273 0.1198 0.1132 0.1072 0.1019","1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400",17.0);
chain3(series4,"1.3334 0.0973 0.0648 0.3333 0.4074 0.2222 0.0278 0.0243 0.1482 0.2037 0.1212 0.0162 0.0150 0.0952 0.1358 0.0833 0.0114 0.0108 0.0702 0.1019","1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20",17.0);
travel3(series0,sweep0,17.0,linear);
travel3(series1,sweep1,17.0,linear);
travel3(series2,sweep2,17.0,linear);
travel3(series3,sweep3,17.0,linear);
travel3(series4,sweep4,17.0,linear);
seq {
wait(0.45);
par {
fade(layer1,0.80); fade(layer2,0.80); fade(layer3,0.80); fade(layer4,0.80);
view3(shot0,"isometric",1.45,smooth,1.08);
say(caption,"Square wave — odd harmonics sharpen every corner.",0.35);
}
wait(1.65);
par {
fade(layer0,0.65); show(layer1,0.65);
view3(shot1,"isometric",1.35,smooth,1.08);
say(caption,"Sawtooth — every harmonic joins with alternating direction.",0.35);
}
wait(1.65);
par {
fade(layer1,0.65); show(layer2,0.65);
view3(shot2,"isometric",1.35,smooth,1.08);
say(caption,"Hann window — the outer harmonics taper away.",0.35);
}
wait(1.65);
par {
fade(layer2,0.65); show(layer3,0.65);
view3(shot3,"isometric",1.35,smooth,1.08);
say(caption,"Quadratic chirp — frequency accelerates as n squared.",0.35);
}
wait(1.65);
par {
fade(layer3,0.65); show(layer4,0.65);
view3(shot4,"isometric",1.35,smooth,1.08);
say(caption,"Envelope ripple — selected harmonics breathe in and out.",0.35);
}
wait(1.55);
par {
show(layer0,0.55); show(layer1,0.55); show(layer2,0.55); show(layer3,0.55);
view3(overviewShot,"isometric",1.5,smooth,1.30);
say(caption,"One relationship engine. Five very different signals.",0.35);
}
}
}
}
wait(0.35);
step("create with Manic") {
show(cta,0.45);
pulse(stack,0.75);
say(caption,"Describe the relationship; Manic keeps every ring, endpoint, history, and camera together.",0.40);
}
wait(1.4);
fourier-square-wave-live
Thirteen orbit rings add odd harmonics 1 through 25. Their live partial histories show the plateaus flatten and the corners sharpen across two uninterrupted square-wave cycles before a creator CTA closes the Short.
// Odd harmonics build a square wave. Every partial sum is a live history of
// the corresponding endpoint, so the corners emerge from the geometry itself.
title("How Odd Harmonics Build a Square Wave");
canvas("9:16");
template("paper");
watermark(mark,(w*0.15,h*0.045),"Made With Manic");
text(kicker,(cx,h*0.075),"FOURIER · ODD HARMONICS");
text(headline,(cx,h*0.115),"Watch a square wave find its corners");
text(caption,(cx,h*0.18),"Start with n = 1. Then add 3, 5, 7 … all the way to 25.");
text(cta,(cx,h*0.925),"TURN IDEAS INTO ANIMATION → 8gwifi.org/manic");
size(kicker,18); color(kicker,dim); bold(kicker); hidden(kicker);
size(headline,31); bold(headline); wrap(headline,w*0.84); hidden(headline);
size(caption,20); color(caption,dim); wrap(caption,w*0.84); hidden(caption);
size(cta,21); color(cta,cyan); bold(cta); hidden(cta);
camera3((-29,-25,43),(1,-2,0),42,orthographic);
collection3(series,(-3,5,0),13,(0,0,0),11,0.065);
line3(sweep,(-3,5,0),(10,5,0)); hidden(sweep);
links3(arms,series,chain);
child3(tip,series,12,0.13);
arrow3(chainX,(-11,5,0),(7,5,0));
arrow3(chainY,(-3,-2,0),(-3,10.5,0));
arrow3(plotX,(-7,-10.5,0),(12,-10.5,0));
arrow3(plotY,(-7,-10.5,0),(-7,-4.2,0));
point3(shotA,(-7.5,-11,0),0.01);
point3(shotB,(8.5,10.5,0),0.01);
tag(shotA,shot); tag(shotB,shot);
hidden(shotA); hidden(shotB);
tag(series,construction); tag(arms,construction); tag(tip,construction);
tag(chainX,construction); tag(chainY,construction);
tag(plotX,construction); tag(plotY,construction);
for i in 0..13 {
ring3(orbit{i},series,i,72);
historyplot3(partial{i},series,i,y,(-7,-10.5,0),(19,6));
thick(orbit{i},0.030);
thick(partial{i},0.020);
tag(orbit{i},construction);
tag(partial{i},construction);
}
equation(formula,(0,0),`S_{13}(t)=\sum_{k=0}^{12}\frac{4}{\pi(2k+1)}\sin((2k+1)t)`,24);
label3(formula,(-4,-2.7,0),0.80);
tag(formula,construction);
color(series,fg); color(arms,fg); color(tip,fg);
color(chainX,fg); color(chainY,fg); color(plotX,fg); color(plotY,fg);
color(formula,fg);
thick(chainX,0.016); thick(chainY,0.016);
thick(plotX,0.016); thick(plotY,0.016);
color(orbit0,lime); color(partial0,lime);
color(orbit1,gold); color(partial1,gold);
color(orbit2,orange); color(partial2,orange);
color(orbit3,coral); color(partial3,coral);
color(orbit4,red); color(partial4,red);
color(orbit5,magenta); color(partial5,magenta);
color(orbit6,violet); color(partial6,violet);
color(orbit7,indigo); color(partial7,indigo);
color(orbit8,blue); color(partial8,blue);
color(orbit9,cyan); color(partial9,cyan);
color(orbit10,mint); color(partial10,mint);
color(orbit11,lime); color(partial11,lime);
color(orbit12,fg); color(partial12,fg);
hidden(construction);
step("begin with one sine wave") {
show(kicker,0.30);
show(headline,0.42);
show(caption,0.38);
}
wait(0.35);
step("reveal the odd-harmonic chain") {
par {
show(construction,0.85);
view3(shot,"top",1.35,smooth,1.08);
say(caption,"Every smaller orbit contributes the next odd frequency.",0.38);
}
}
wait(0.30);
step("build the corners live") {
par {
// Two complete base cycles: all odd harmonic rates are doubled together.
chain3(series,"4.0744 1.3581 0.8149 0.5821 0.4527 0.3704 0.3134 0.2716 0.2397 0.2144 0.1940 0.1771 0.1630","2 6 10 14 18 22 26 30 34 38 42 46 50",16.0);
travel3(series,sweep,16.0,linear);
seq {
say(caption,"Cycle one: odd harmonics flatten the plateaus and steepen both sides.",0.40);
wait(6.10);
say(caption,"Cycle two: every orbit closes and the square-wave pattern returns continuously.",0.40);
wait(5.20);
say(caption,"Two complete periods make the repeating structure impossible to miss.",0.40);
}
}
}
wait(0.35);
step("create with Manic") {
par {
pulse(tip,0.75);
pulse(partial12,0.75);
show(cta,0.45);
say(caption,"One declarative chain becomes circles, partial sums, a live waveform, and a complete visual story.",0.42);
}
}
wait(1.45);
growing-fractal-tree3
One seeded split-turn-shrink rule grows 1,023 branches. tree3 keeps each generation
renderer-batched while progressive draw and a bounds-aware camera reveal the canopy.
title("One Rule Grows a Forest");
canvas("9:16");
template("blank");
watermark(mark,(w*0.16,h*0.055),"Made With Manic");
text(kicker,(cx,h*0.085),"FRACTAL TREE · BATCHED GENERATIONS");
text(headline,(cx,h*0.13),"How can one branch become 1,023?");
text(caption,(cx,h*0.84),"Repeat one rule: split, turn, shrink—and keep every generation readable.");
text(cta,(cx,h*0.91),"CREATE YOUR OWN → 8gwifi.org/manic");
size(kicker,20); color(kicker,dim); bold(kicker); hidden(kicker);
size(headline,34); bold(headline); wrap(headline,w*0.84); hidden(headline);
size(caption,22); color(caption,dim); wrap(caption,w*0.82); hidden(caption);
size(cta,22); color(cta,cyan); bold(cta); hidden(cta);
camera3((9,-13,8),(0,0,3.2),43);
tree3(tree,(0,0,-3.2),2.1,27,0.72,10,42);
for i in 0..10 { untraced(tree.d{i}); hidden(tree.d{i}); }
hidden(tree.leaves);
step("the seed") {
show(kicker,0.3); show(headline,0.45); show(caption,0.4);
view3(tree,"isometric",0.7,smooth,1.42);
}
wait(0.35);
step("grow by generation") {
par {
stagger(0.16) {
for i in 0..10 {
par { show(tree.d{i},0.22); draw(tree.d{i},0.58,smooth); }
}
}
say(caption,"Each depth is one renderer batch, even when it contains hundreds of branches.",0.45);
view3(tree,"isometric",1.1,smooth,1.48);
}
}
wait(0.4);
step("find the canopy") {
par {
show(tree.leaves,0.65);
seq {
orbit3(42,24,21.0,2.1,smooth);
view3(tree,"isometric",0.9,smooth,1.42);
}
say(caption,"A semantic camera journey reveals the rule first, then the whole canopy.",0.45);
}
}
wait(0.5);
step("takeaway") {
par {
pulse(tree.leaves,0.7);
say(caption,"Deep procedural geometry stays simple to author, deterministic, and smooth to seek.",0.45);
show(cta,0.45);
}
}
wait(1.3);
hilbert-curve3-space-filling
One continuous 3D Hilbert path refines from 7 to 32,767 segments while keeping its identity, arc-length colour, cube bounds, and uninterrupted orbit camera. The generated curve visits every lattice cell exactly once at each order.
// Engine Test 7 — a 3D Hilbert curve refines from 7 to 32,767 segments.
// One generated path keeps its identity while become3, draw, and orbit3 tell
// the story. The curve is exact: each order visits every lattice cell once.
title("Hilbert Curve in 3D");
canvas("16:9");
template("black");
creator(me,"@anish2good name=Manic_Math tagline=Space_filling_curves_made_visible yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=magenta secondary=cyan footer=compact cta=Animate_the_infinite safe=clean");
watermark(mark,(105,52),"Made With Manic");
endcard(me,"title=Make_Fractals_Move cta=Create_with_Manic");
text(kicker,(cx,46),"HILBERT CURVE IN 3D");
text(head,(cx,86),"How can one line fill a cube?");
text(caption,(cx,626),"Begin with one continuous route through all eight octants.");
text(orderLabel,(cx,668),"ORDER 1 · 7 SEGMENTS");
size(kicker,20); bold(kicker); color(kicker,magenta);
size(head,34); bold(head);
size(caption,21); color(caption,dim);
size(orderLabel,19); bold(orderLabel);
camera3((13,-15,11),(0,0,0),42);
// A quiet wireframe establishes the volume before the curve enters it.
line3(e0,(-3,-3,-3),(3,-3,-3)); line3(e1,(3,-3,-3),(3,3,-3));
line3(e2,(3,3,-3),(-3,3,-3)); line3(e3,(-3,3,-3),(-3,-3,-3));
line3(e4,(-3,-3,3),(3,-3,3)); line3(e5,(3,-3,3),(3,3,3));
line3(e6,(3,3,3),(-3,3,3)); line3(e7,(-3,3,3),(-3,-3,3));
line3(e8,(-3,-3,-3),(-3,-3,3)); line3(e9,(3,-3,-3),(3,-3,3));
line3(e10,(3,3,-3),(3,3,3)); line3(e11,(-3,3,-3),(-3,3,3));
tag(e0,bounds); tag(e1,bounds); tag(e2,bounds); tag(e3,bounds);
tag(e4,bounds); tag(e5,bounds); tag(e6,bounds); tag(e7,bounds);
tag(e8,bounds); tag(e9,bounds); tag(e10,bounds); tag(e11,bounds);
color(bounds,dim); opacity(bounds,0.34); thick(bounds,0.012);
hidden(bounds); untraced(bounds);
// The five exact refinements. Hidden curves are blueprints for become3.
hilbert3(curve,(0,0,0),5.65,1);
hilbert3(order2,(0,0,0),5.65,2);
hilbert3(order3,(0,0,0),5.65,3);
hilbert3(order4,(0,0,0),5.65,4);
hilbert3(order5,(0,0,0),5.65,5);
hidden(curve); untraced(curve);
hidden(order2); hidden(order3); hidden(order4); hidden(order5);
hidden(kicker); hidden(head); hidden(caption); hidden(orderLabel);
step("pose the question") {
par {
show(kicker,0.30);
show(head,0.45);
show(caption,0.40);
show(orderLabel,0.35);
show(bounds,0.35);
draw(bounds,1.25,smooth);
}
}
wait(0.45);
step("visit the eight octants") {
par {
show(curve,0.30);
draw(curve,1.65,smooth);
orbit3(54,25,16.2,1.65,smooth);
}
}
wait(0.45);
step("refine the route") {
par {
seq {
par {
become3(curve,order2,1.30,smooth);
say(orderLabel,"ORDER 2 · 63 SEGMENTS",0.35,smooth);
say(caption,"Replace every visit with a smaller, rotated copy—without breaking the line.",0.45,smooth);
}
wait(0.35);
par {
become3(curve,order3,1.55,smooth);
say(orderLabel,"ORDER 3 · 511 SEGMENTS",0.35,smooth);
say(caption,"The gaps shrink while continuity survives every refinement.",0.45,smooth);
}
wait(0.35);
par {
become3(curve,order4,1.90,smooth);
say(orderLabel,"ORDER 4 · 4,095 SEGMENTS",0.35,smooth);
say(caption,"One path now threads through thousands of cells in the cube.",0.45,smooth);
}
wait(0.35);
par {
become3(curve,order5,2.40,smooth);
say(orderLabel,"ORDER 5 · 32,767 SEGMENTS",0.35,smooth);
say(caption,"In the limit, the continuous curve reaches every point in the volume.",0.50,smooth);
}
}
orbit3(186,31,17.8,8.55,linear);
}
}
wait(0.80);
step("takeaway") {
par {
pulse(curve,0.80);
say(head,"A one-dimensional path can fill 3D space.",0.50,smooth);
say(caption,"Refinement adds detail—not disconnected pieces.",0.45,smooth);
}
}
wait(1.35);
step("cta") {
par {
fade(kicker,0.30); fade(head,0.30); fade(caption,0.30);
fade(orderLabel,0.30); fade(bounds,0.40); fade(curve,0.45);
show(me.endcard,0.60);
}
}
wait(2.00);
latex-disintegration
A quadratic factors into two solutions, then those symbols become persistent points at
2 and 3. Semantic rewrite preserves the algebra; disintegrate removes the notation
while the mathematical meaning remains visibly in place.
// A semantic LaTeX story: notation changes, meaning becomes position, and the
// final symbols may disappear without taking the mathematical result with them.
title("The Equation Disappears. The Roots Remain.");
canvas("9:16");
template("blank");
watermark(mark,(w*0.16,h*0.050),"Made With Manic");
text(kicker,(cx,h*0.085),"ALGEBRA · SYMBOLS → STRUCTURE");
text(headline,(cx,h*0.145),"What survives when an equation disappears?");
text(caption,(cx,h*0.77),"Two solutions are hiding inside one quadratic.");
text(cta,(cx,h*0.90),"MAKE MEANING MOVE → 8gwifi.org/manic");
size(kicker,19); color(kicker,dim); bold(kicker); hidden(kicker);
size(headline,33); bold(headline); wrap(headline,w*0.84); hidden(headline);
size(caption,22); color(caption,dim); wrap(caption,w*0.82); hidden(caption);
size(cta,22); color(cta,cyan); bold(cta); hidden(cta);
text(symbolLabel,(w*0.18,h*0.29),"THE SYMBOLS");
text(meaningLabel,(w*0.18,h*0.54),"THE MEANING");
size(symbolLabel,18); color(symbolLabel,dim); bold(symbolLabel); hidden(symbolLabel);
size(meaningLabel,18); color(meaningLabel,dim); bold(meaningLabel); hidden(meaningLabel);
equation(work,(cx,h*0.39),`x^2-5x+6=0`,66);
hidden(work);
let axisY = h*0.63;
let axisLeft = w*0.16;
let axisRight = w*0.84;
let x0 = w*0.20;
let dx = w*0.15;
arrow(numberAxis,(axisLeft,axisY),(axisRight,axisY));
color(numberAxis,dim); stroke(numberAxis,3);
tag(numberAxis,numberLine);
for i in 0..5 {
line(tick{i},(x0+i*dx,axisY-13),(x0+i*dx,axisY+13));
color(tick{i},dim); stroke(tick{i},3);
tag(tick{i},numberLine);
}
text(n0,(x0,axisY+42),"0");
text(n1,(x0+dx,axisY+42),"1");
text(n2,(x0+2*dx,axisY+42),"2");
text(n3,(x0+3*dx,axisY+42),"3");
text(n4,(x0+4*dx,axisY+42),"4");
size(n0,18); size(n1,18); size(n2,18); size(n3,18); size(n4,18);
color(n0,dim); color(n1,dim); color(n2,dim); color(n3,dim); color(n4,dim);
tag(n0,numberLine); tag(n1,numberLine); tag(n2,numberLine);
tag(n3,numberLine); tag(n4,numberLine);
circle(root2,(x0+2*dx,axisY),15);
circle(root3,(x0+3*dx,axisY),15);
filled(root2); filled(root3);
color(root2,cyan); color(root3,orange);
glow(root2,0.72); glow(root3,0.72);
tag(root2,solutions); tag(root3,solutions);
equation(root2Label,(x0+2*dx,axisY-62),`x=2`,30);
equation(root3Label,(x0+3*dx,axisY-62),`x=3`,30);
color(root2Label,cyan); color(root3Label,orange);
tag(root2Label,solutions); tag(root3Label,solutions);
hidden(numberLine);
hidden(solutions);
step("find what is hiding") {
par {
show(kicker,0.30);
show(headline,0.45);
show(symbolLabel,0.35);
show(caption,0.40);
show(work,0.55);
}
}
wait(0.55);
step("expose the two conditions") {
par {
rewrite(work,`\textcolor{cyan}{(x-2)}\textcolor{orange}{(x-3)}=0`,1.15,smooth);
say(caption,"Factoring reveals two independent ways for the product to become zero.",0.45);
}
}
wait(0.55);
step("turn answers into positions") {
par {
rewrite(work,`\textcolor{cyan}{x=2}\qquad\text{or}\qquad\textcolor{orange}{x=3}`,1.05,smooth);
seq {
show(meaningLabel,0.30);
show(numberLine,0.55);
show(root2,0.35);
show(root2Label,0.30);
wait(0.18);
show(root3,0.35);
show(root3Label,0.30);
}
say(caption,"The answers are no longer only symbols—they now occupy exact locations.",0.45);
}
}
wait(0.70);
step("remove the notation, keep the result") {
par {
disintegrate(work,1.60);
seq {
wait(0.35);
pulse(root2,0.60);
pulse(root3,0.60);
}
say(caption,"The notation can turn to dust. The two solutions remain exactly where they belong.",0.45);
}
}
wait(0.75);
step("create explanations that preserve meaning") {
par {
pulse(solutions,0.75);
show(cta,0.45);
say(caption,"Great visual explanations do more than move symbols—they reveal what those symbols mean.",0.45);
}
}
wait(1.45);
story-time-varying-field3
A seed cloud enters a 3D vector field whose formula changes with normalized time. Deterministic advection, live arrows, one addressable particle, its trail, and a follow camera all sample the same seekable timeline.
title("The Current Changes While We Travel");
canvas("9:16");
template("blank");
watermark(mark, (w*0.16, h*0.055), "Made With Manic");
text(kicker, (cx, h*0.085), "ENGINE FOUNDATION · LIVE 3D FIELDS");
text(headline, (cx, h*0.13), "What if the current changes mid-flight?");
text(caption, (cx, h*0.83), "The arrows and particles sample the same field at the same story time.");
size(kicker,20); color(kicker,dim); bold(kicker); hidden(kicker);
size(headline,34); bold(headline); wrap(headline,w*0.84); hidden(headline);
size(caption,22); color(caption,dim); wrap(caption,w*0.80); hidden(caption);
camera3((16,-20,14),(0,0,0),46);
vectorfield3(current,(0,0,0),(4.2,3.2,2.6),"-y + 0.7*sin(p*tau)","x + 0.35*cos(p*tau)","0.45*sin(z+p*tau)",6);
collection3(tracers,(0,0,0),90,(2.5,1.8,1.5),73,0.065);
child3(hero,tracers,0,0.14);
trail3(route,tracers,0,0.032);
color(current,dim);
color(tracers,cyan);
color(hero,gold);
color(route,gold);
hidden(current); hidden(tracers); hidden(hero); hidden(route);
step("question") {
show(kicker,0.35);
show(headline,0.45);
show(caption,0.40);
}
wait(0.35);
step("reveal-the-current") {
par {
show(current,0.70);
show(tracers,0.55);
show(hero,0.45);
show(route,0.45);
say(caption,"A bounded formula declares x, y, z and normalized time p.",0.40);
}
}
wait(0.30);
step("flow-and-follow") {
followshot3(hero,(0,0,0.15));
par {
advect3(tracers,current,7.0,1.15);
orbit3(55,26,29.0,7.0,smooth);
say(caption,"Deterministic RK4 moves every tracer while the camera follows one stable child.",0.45);
}
followshot3(none);
}
wait(0.45);
step("takeaway") {
cue(chime);
say(caption,"Field, motion, trail, and shot now tell one synchronized truth.",0.45);
}
wait(1.2);
story-addressable-asset-cues
A grouped console OBJ becomes an addressable technical assembly. Its base, screen, and key reveal as named parts with projected notation, a local procedural cue, and particle punctuation—no remote asset or callback.
title("An Asset Becomes a Story");
canvas("9:16");
template("mono");
watermark(mark, (w*0.16, h*0.055), "Made With Manic");
text(kicker, (cx, h*0.085), "ENGINE FOUNDATION · ASSETS, LABELS & CUES");
text(headline, (cx, h*0.15), "An imported object should still be explainable");
text(caption, (cx, h*0.82), "Groups become addressable parts—then motion, notation, sound, and effects compose normally.");
size(kicker,18); color(kicker,dim); bold(kicker); hidden(kicker);
size(headline,34); bold(headline); wrap(headline,w*0.84); hidden(headline);
size(caption,21); color(caption,dim); wrap(caption,w*0.82); hidden(caption);
camera3((16,-22,15),(0,0,0),46);
assembly3(console,"asset:models/manic-console.obj",(0,0,0),1.35);
color(console.base,dim);
color(console.screen,cyan);
color(console.key,magenta);
text(screenNote,(0,0),"world-sized display");
text(keyNote,(0,0),"addressable input");
size(screenNote,22); color(screenNote,cyan); hidden(screenNote);
size(keyNote,22); color(keyNote,magenta); hidden(keyNote);
label3(screenNote,console.screen,0.82);
label3(keyNote,console.key,0.82);
hidden(console);
circle(effectBox,(cx,h*0.70),72);
particles(sparks,effectBox,34,4,19);
color(sparks,gold);
hidden(effectBox); hidden(sparks);
step("import") {
show(kicker,0.35);
show(headline,0.45);
show(caption,0.40);
show(console,0.75);
cue(whoosh);
}
wait(0.35);
step("address-the-parts") {
seq {
show(screenNote,0.35);
pulse(console.screen,0.65);
fade(screenNote,0.25);
show(keyNote,0.35);
pulse(console.key,0.65);
}
say(caption,"OBJ groups arrive as console.base, console.screen, and console.key.",0.42);
cue(tick);
}
wait(0.35);
step("compose-the-explanation") {
par {
turn3(console,(0,0,0),z,22,1.4,smooth);
orbit3(30,28,30,1.4,smooth);
say(caption,"Depth-scaled labels remain crisp while the grouped asset moves.",0.40);
}
}
wait(0.30);
step("finish-with-a-beat") {
show(sparks,0.15);
par {
burst(sparks,0.9);
pulse(console.key,0.65);
say(caption,"A reusable cue and the existing generic burst add the final editorial beat.",0.40);
}
cue(pop);
}
wait(1.2);
trapped-light-dimensions
A photon escapes one dimension at a time: first a 5-unit line, then the 5–12–13 diagonal of a plane, and finally the 13–84–85 diagonal through a volume. One persistent light beam makes the generalized Pythagorean idea visible.
// ============================================================================
// trapped-light-dimensions.manic — a continuous 1D → 2D → 3D journey
// ----------------------------------------------------------------------------
// One photon and one equation persist through the whole story. A fiber sweeps
// sideways into a glass floor; that same floor rises into a transparent room.
// The visual construction makes the distance formula grow one perpendicular
// square at a time instead of presenting three unrelated formulas.
// ============================================================================
title("The Trapped Light Beam — From 1D to 3D");
canvas("9:16");
template("neon");
watermark(manicMark, (238, 108), "Made With Manic");
creator(me, "@anish2good name=Manic_3D tagline=Build_dimensions_visually yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=cyan secondary=magenta footer=social safe=reels");
socials(me);
text(kicker, (540, 164), "A JOURNEY THROUGH DIMENSIONS");
size(kicker, 21); bold(kicker); color(kicker, cyan); hidden(kicker);
text(headline, (540, 228), "The trapped light beam");
size(headline, 43); bold(headline); color(headline, fg); hidden(headline);
text(dimension, (540, 300), "1D · A THREAD IN THE DARK");
size(dimension, 23); bold(dimension); color(dimension, gold); hidden(dimension);
text(narration, (540, 1328), "No up. No down. Only backwards and forwards.");
size(narration, 27); bold(narration); color(narration, fg); wrap(narration, 860); hidden(narration);
text(insight, (540, 1386), "One permitted direction");
size(insight, 20); color(insight, dim); hidden(insight);
equation(distance, (540, 1482), `L=\left|x_2-x_1\right|`, 39);
hidden(distance);
// ---------------------------------------------------------------------------
// THE SPATIAL CAST
// ---------------------------------------------------------------------------
camera3((9, -16, 8), (2.5, 0, 0), 38);
// 1D: a five-unit fiber and one persistent photon.
line3(fiber, (0,0,0), (5,0,0)); color(fiber, cyan); thick(fiber, 0.065); untraced(fiber);
point3(A, (0,0,0), 0.16); color(A, fg); hidden(A);
point3(B, (5,0,0), 0.16); color(B, fg); hidden(B);
point3(photon, (0,0,0), 0.22); color(photon, gold); hidden(photon);
finish3(photon, "material=glass shading=smooth depth=0.35 shadow=0.20");
text(aLabel, (0,0), "A · x = 0"); size(aLabel, 21); color(aLabel, fg); hidden(aLabel); pin3(aLabel, A);
text(bLabel, (0,0), "B · x = 5"); size(bLabel, 21); color(bLabel, fg); hidden(bLabel); pin3(bLabel, B);
point3(xMid, (2.5,0,0), 0.01); hidden(xMid);
text(xLabel, (0,0), "a = 5"); size(xLabel, 22); bold(xLabel); color(xLabel, cyan); hidden(xLabel); pin3(xLabel, xMid);
// 2D: pull a copy of the fiber along y. The shallow cuboid is the glass floor.
line3(sweepLine, (0,0,0), (5,0,0)); color(sweepLine, magenta); thick(sweepLine, 0.045); hidden(sweepLine);
line3(yEdge, (0,0,0), (0,12,0)); color(yEdge, magenta); thick(yEdge, 0.045); untraced(yEdge); hidden(yEdge);
point3(yMid, (0,6,0), 0.01); hidden(yMid);
text(yLabel, (0,0), "b = 12"); size(yLabel, 22); bold(yLabel); color(yLabel, magenta); hidden(yLabel); pin3(yLabel, yMid);
cube3(glassFloor, (2.5,6,0), (5,12,0.12)); color(glassFloor, blue); hidden(glassFloor);
finish3(glassFloor, "material=glass shading=flat mesh=0.42 depth=0.30 shadow=0.12");
line3(floorDiagonal, (0,0,0.08), (5,12,0.08)); color(floorDiagonal, gold); thick(floorDiagonal, 0.075); untraced(floorDiagonal); hidden(floorDiagonal);
point3(floorTarget, (5,12,0.08), 0.19); color(floorTarget, fg); hidden(floorTarget);
text(floorTargetLabel, (0,0), "(5, 12)"); size(floorTargetLabel, 20); color(floorTargetLabel, fg); hidden(floorTargetLabel); pin3(floorTargetLabel, floorTarget);
point3(floorMid, (2.5,6,0.08), 0.01); hidden(floorMid);
text(floorPathLabel, (0,0), "base = 13"); size(floorPathLabel, 21); bold(floorPathLabel); color(floorPathLabel, gold); hidden(floorPathLabel); pin3(floorPathLabel, floorMid);
// 3D: the flat floor becomes a transparent room. The 84-unit height is
// compressed to 18 visual units so the reused 13-unit floor diagonal remains
// readable; labels and mathematics retain the real coordinates. Four persistent
// roof edges rise as one tagged rig while the transparent volume resolves.
cube3(room, (2.5,6,9), (5,12,18)); color(room, blue); hidden(room);
finish3(room, "material=glass shading=flat mesh=0.50 depth=0.38 shadow=0.16");
line3(roofFront, (0,0,0), (5,0,0)); color(roofFront, cyan); thick(roofFront, 0.055); hidden(roofFront); tag(roofFront, roof);
line3(roofBack, (0,12,0), (5,12,0)); color(roofBack, cyan); thick(roofBack, 0.055); hidden(roofBack); tag(roofBack, roof);
line3(roofLeft, (0,0,0), (0,12,0)); color(roofLeft, cyan); thick(roofLeft, 0.055); hidden(roofLeft); tag(roofLeft, roof);
line3(roofRight, (5,0,0), (5,12,0)); color(roofRight, cyan); thick(roofRight, 0.055); hidden(roofRight); tag(roofRight, roof);
line3(zEdge, (5,12,0), (5,12,18)); color(zEdge, lime); thick(zEdge, 0.08); untraced(zEdge); hidden(zEdge);
point3(zMid, (5,12,9), 0.01); hidden(zMid);
text(zLabel, (0,0), "c = 84"); size(zLabel, 22); bold(zLabel); color(zLabel, lime); hidden(zLabel); pin3(zLabel, zMid);
line3(spaceDiagonal, (0,0,0), (5,12,18)); color(spaceDiagonal, gold); thick(spaceDiagonal, 0.10); untraced(spaceDiagonal); hidden(spaceDiagonal);
point3(spaceTarget, (5,12,18), 0.24); color(spaceTarget, fg); hidden(spaceTarget);
text(spaceTargetLabel, (0,0), "(5, 12, 84)"); size(spaceTargetLabel, 20); color(spaceTargetLabel, fg); hidden(spaceTargetLabel); pin3(spaceTargetLabel, spaceTarget);
// ---------------------------------------------------------------------------
// THE JOURNEY
// ---------------------------------------------------------------------------
step("one dimension") {
seq {
par {
show(kicker, 0.35);
show(headline, 0.55);
show(dimension, 0.40);
show(narration, 0.45);
show(insight, 0.45);
show(distance, 0.55);
view3(fiber, "front", 1.0, smooth, 1.65);
}
stagger(0.12) {
show(A, 0.30);
show(aLabel, 0.30);
draw(fiber, 1.10, smooth);
show(B, 0.30);
show(bLabel, 0.30);
show(xLabel, 0.30);
show(photon, 0.35);
}
wait(0.45);
}
}
step("the bound beam") {
seq {
say(narration, "Fire one photon from A to B. There is nowhere else to go.", 0.50, smooth);
par {
travel3(photon, fiber, 2.25, smooth);
rewrite(distance, `L=\left|5-0\right|=\textcolor{gold}{5}`, 1.10, smooth);
say(insight, "Distance is the length of the only available track", 0.45, smooth);
}
pulse(photon, 0.70);
wait(0.55);
}
}
step("unlock the second direction") {
seq {
par {
move3(photon, (0,0,0), 0.80, smooth);
fade(aLabel, 0.35);
fade(bLabel, 0.35);
say(dimension, "2D · UNFOLDING THE FLOOR", 0.45, smooth);
say(narration, "Pull the fiber sideways. One line sweeps out a reflective surface.", 0.55, smooth);
say(insight, "A new perpendicular direction appears: y", 0.45, smooth);
}
par {
show(yEdge, 0.15);
draw(yEdge, 1.10, smooth);
show(yLabel, 0.35);
view3(glassFloor, "isometric", 1.35, smooth, 1.55);
}
par {
show(sweepLine, 0.25);
move3(sweepLine, (0,12,0), 1.90, smooth);
show(glassFloor, 1.90, smooth);
}
to(glassFloor, opacity, 0.22, 0.40, smooth);
wait(0.45);
}
}
step("cut across the floor") {
seq {
par {
show(floorTarget, 0.30);
show(floorTargetLabel, 0.35);
show(floorPathLabel, 0.35);
say(narration, "The photon can turn now — but the straight diagonal is shortest.", 0.55, smooth);
say(insight, "The old distance and the new sweep meet at 90 degrees", 0.45, smooth);
}
par {
show(floorDiagonal, 0.15);
draw(floorDiagonal, 2.35, smooth);
travel3(photon, floorDiagonal, 2.35, smooth);
rewrite(distance, `L=\sqrt{\textcolor{cyan}{5^2}+\textcolor{magenta}{12^2}}=\sqrt{169}=\textcolor{gold}{13}`, 1.35, smooth);
}
pulse(photon, 0.70);
wait(0.65);
}
}
step("raise the roof") {
seq {
par {
move3(photon, (0,0,0), 0.90, smooth);
fade(xLabel, 0.35);
fade(yLabel, 0.35);
fade(floorTargetLabel, 0.35);
say(dimension, "3D · FILLING THE VOID", 0.45, smooth);
say(narration, "Lift the entire floor upward. Surface becomes volume.", 0.55, smooth);
say(insight, "Height compressed visually · coordinates and geometry stay exact", 0.45, smooth);
}
par {
to(room, opacity, 0.22, 2.45, smooth);
show(roof, 0.20);
shift3(roof, (0,0,18), 2.45, smooth);
view3(room, "isometric", 2.45, smooth, 1.62);
show(zEdge, 0.15);
draw(zEdge, 2.30, smooth);
show(zLabel, 0.45);
}
par { show(spaceTarget, 0.40); show(spaceTargetLabel, 0.40); }
wait(0.55);
}
}
step("cut through space") {
seq {
say(narration, "The photon cuts through open space: floor diagonal against height.", 0.55, smooth);
par {
show(spaceDiagonal, 0.15);
draw(spaceDiagonal, 2.80, smooth);
travel3(photon, spaceDiagonal, 2.80, smooth);
rewrite(distance, `L=\sqrt{\textcolor{gold}{13^2}+\textcolor{lime}{84^2}}=\sqrt{7225}=\textcolor{gold}{85}`, 1.50, smooth);
say(insight, "13² + 84² = 85²", 0.45, smooth);
}
pulse(photon, 0.80);
wait(0.70);
}
}
step("one recursive idea") {
seq {
par {
say(dimension, "THE PATTERN · ONE RIGHT ANGLE AT A TIME", 0.50, smooth);
say(narration, "Do not memorize three formulas. Build one distance recursively.", 0.55, smooth);
say(insight, "Each previous diagonal becomes the next dimension's base", 0.50, smooth);
rewrite(distance, `L=\sqrt{\textcolor{cyan}{a^2}+\textcolor{magenta}{b^2}+\textcolor{lime}{c^2}}`, 1.65, smooth);
view3(room, "fit", 2.20, smooth, 1.70);
}
par {
pulse(photon, 0.80);
flash(spaceDiagonal, gold);
roll3(3, 1.60, smooth);
}
wait(2.20);
}
}
dimensions-unfold
A point stretches into a line, the line sweeps sideways into a plane, and the plane lifts into a room. The geometry grows continuously instead of resetting between 1D, 2D, and 3D.
// ============================================================================
// dimensions-unfold.manic — how a point becomes a line, a plane, then a room
// ----------------------------------------------------------------------------
// The geometry never cuts to a replacement scene. One point grows two halves
// of a wire; that same wire sweeps across y while a grid resolves behind it;
// the completed sheet then lifts as one tagged roof while a transparent room
// appears below. The animation is the explanation.
// ============================================================================
title("How Space Learned to Grow — 1D to 3D");
canvas("9:16");
template("neon");
watermark(manicMark, (238, 108), "Made With Manic");
creator(me, "@anish2good name=Manic_3D tagline=Dimensions_in_motion yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=cyan secondary=magenta footer=social safe=reels");
socials(me);
text(kicker, (540, 164), "FROM A POINT TO A WORLD");
size(kicker, 21); bold(kicker); color(kicker, cyan); hidden(kicker);
text(headline, (540, 228), "How space learned to grow");
size(headline, 42); bold(headline); color(headline, fg); hidden(headline);
text(phase, (540, 300), "PROLOGUE · THE FIRST POINT");
size(phase, 23); bold(phase); color(phase, gold); hidden(phase);
text(narration, (540, 1330), "In the beginning, there was only one location.");
size(narration, 27); bold(narration); color(narration, fg); wrap(narration, 860); hidden(narration);
text(principle, (540, 1390), "No direction. No distance. Just here.");
size(principle, 20); color(principle, dim); hidden(principle);
text(progress, (540, 1492), "POINT");
size(progress, 27); bold(progress); color(progress, cyan); hidden(progress);
text(takeaway, (540, 1554), "DIMENSIONS ARE MOTIONS THAT LEAVE SPACE BEHIND");
size(takeaway, 17); bold(takeaway); color(takeaway, gold); hidden(takeaway);
// ---------------------------------------------------------------------------
// 0D → 1D: one point extends left and right.
// ---------------------------------------------------------------------------
camera3((0, -11, 4.5), (0, -4, 0), 39);
point3(seed, (0,-4,0), 0.12); color(seed, gold); hidden(seed);
finish3(seed, "material=glass shading=smooth depth=0.40 shadow=0.25");
line3(leftThread, (0,-4,0), (-0.02,-4,0)); color(leftThread, cyan); thick(leftThread, 0.065); hidden(leftThread); tag(leftThread, wire); tag(leftThread, sheetSkin);
line3(rightThread, (0,-4,0), (0.02,-4,0)); color(rightThread, cyan); thick(rightThread, 0.065); hidden(rightThread); tag(rightThread, wire); tag(rightThread, sheetSkin);
point3(leftEnd, (-4,-4,0), 0.14); color(leftEnd, fg); hidden(leftEnd);
point3(rightEnd, (4,-4,0), 0.14); color(rightEnd, fg); hidden(rightEnd);
point3(leftLabelAnchor, (-3.25,-4,0.58), 0.01); hidden(leftLabelAnchor);
point3(rightLabelAnchor, (3.25,-4,0.58), 0.01); hidden(rightLabelAnchor);
text(leftLabel, (0,0), "BACKWARD"); size(leftLabel, 19); bold(leftLabel); color(leftLabel, dim); hidden(leftLabel); pin3(leftLabel, leftLabelAnchor);
text(rightLabel, (0,0), "FORWARD"); size(rightLabel, 19); bold(rightLabel); color(rightLabel, dim); hidden(rightLabel); pin3(rightLabel, rightLabelAnchor);
arrow3(leftChoice, (0,-4,0.18), (-3.5,-4,0.18)); color(leftChoice, magenta); thick(leftChoice, 0.035); untraced(leftChoice); hidden(leftChoice);
arrow3(rightChoice, (0,-4,0.18), (3.5,-4,0.18)); color(rightChoice, magenta); thick(rightChoice, 0.035); untraced(rightChoice); hidden(rightChoice);
// ---------------------------------------------------------------------------
// 2D: the wire sweeps from y=-4 to y=4 and leaves a sheet/grid behind.
// Every visible grid member joins sheetSkin so the whole plane can later rise.
// ---------------------------------------------------------------------------
line3(baseEdge, (-4,-4,0), (4,-4,0)); color(baseEdge, cyan); thick(baseEdge, 0.050); hidden(baseEdge); tag(baseEdge, sheetSkin);
cube3(sheet, (0,0,0), (8,8,0.08)); color(sheet, blue); hidden(sheet); tag(sheet, sheetSkin);
finish3(sheet, "material=glass shading=flat mesh=0.18 depth=0.24 shadow=0.10");
for i in 1..8 {
let p = -4+i;
line3(row{i}, (-4,p,0.05), (4,p,0.05));
color(row{i}, magenta); thick(row{i}, 0.022); untraced(row{i}); hidden(row{i}); tag(row{i}, sheetGrid); tag(row{i}, sheetSkin);
line3(col{i}, (p,-4,0.05), (p,4,0.05));
color(col{i}, cyan); thick(col{i}, 0.022); untraced(col{i}); hidden(col{i}); tag(col{i}, sheetGrid); tag(col{i}, sheetSkin);
}
// ---------------------------------------------------------------------------
// 3D: the complete sheet becomes the roof. Four pillars and a restrained glass
// volume make the hollow space below legible without a replacement cut.
// ---------------------------------------------------------------------------
cube3(room, (0,0,4), (8,8,8)); color(room, blue); hidden(room);
finish3(room, "material=glass shading=flat mesh=0.42 depth=0.34 shadow=0.16");
line3(pillar0, (-4,-4,0), (-4,-4,8)); color(pillar0, lime); thick(pillar0, 0.060); untraced(pillar0); hidden(pillar0); tag(pillar0, pillars);
line3(pillar1, (4,-4,0), (4,-4,8)); color(pillar1, lime); thick(pillar1, 0.060); untraced(pillar1); hidden(pillar1); tag(pillar1, pillars);
line3(pillar2, (-4,4,0), (-4,4,8)); color(pillar2, lime); thick(pillar2, 0.060); untraced(pillar2); hidden(pillar2); tag(pillar2, pillars);
line3(pillar3, (4,4,0), (4,4,8)); color(pillar3, lime); thick(pillar3, 0.060); untraced(pillar3); hidden(pillar3); tag(pillar3, pillars);
point3(nearCorner, (-4,-4,0), 0.18); color(nearCorner, gold); hidden(nearCorner);
point3(farCorner, (4,4,8), 0.18); color(farCorner, gold); hidden(farCorner);
text(nearLabel, (0,0), "NEAR · BOTTOM · LEFT"); size(nearLabel, 18); color(nearLabel, fg); hidden(nearLabel); pin3(nearLabel, nearCorner);
text(farLabel, (0,0), "FAR · TOP · RIGHT"); size(farLabel, 18); color(farLabel, fg); hidden(farLabel); pin3(farLabel, farCorner);
// ---------------------------------------------------------------------------
// ONE CONTINUOUS TIMELINE
// ---------------------------------------------------------------------------
step("the first point") {
seq {
par {
show(kicker, 0.35);
show(headline, 0.55);
show(phase, 0.40);
show(narration, 0.45);
show(principle, 0.45);
show(progress, 0.40);
show(seed, 0.45);
}
pulse(seed, 0.80);
wait(0.65);
}
}
step("the point extends") {
seq {
par {
say(phase, "1D · THE FIRST THREAD", 0.45, smooth);
say(narration, "The point stretches itself out and creates the first path.", 0.55, smooth);
say(principle, "Length appears — but the world is still trapped on one track.", 0.50, smooth);
say(progress, "POINT → LENGTH", 0.45, smooth);
show(leftThread, 0.15);
show(rightThread, 0.15);
grow3(leftThread, (-4,-4,0), 2.25, smooth);
grow3(rightThread, (4,-4,0), 2.25, smooth);
view3(wire, "front", 1.45, smooth, 1.70);
}
par {
fade(seed, 0.35);
show(leftEnd, 0.30); show(rightEnd, 0.30);
show(leftLabel, 0.35); show(rightLabel, 0.35);
show(leftChoice, 0.15); show(rightChoice, 0.15);
draw(leftChoice, 0.90, smooth); draw(rightChoice, 0.90, smooth);
}
wait(0.60);
}
}
step("left or right") {
seq {
say(narration, "Every journey has only two choices: backward or forward.", 0.50, smooth);
par { pulse(leftThread, 0.65); pulse(rightThread, 0.65); }
wait(0.80);
}
}
step("the sideways sweep") {
seq {
par {
fade(leftChoice, 0.35); fade(rightChoice, 0.35);
fade(leftLabel, 0.35); fade(rightLabel, 0.35);
say(phase, "2D · THE UNFOLDING SHEET", 0.45, smooth);
say(narration, "The wire shimmers, grabs its whole length, and moves sideways.", 0.55, smooth);
say(principle, "Its motion leaves a flat floor behind: width is born.", 0.50, smooth);
say(progress, "POINT → LENGTH → WIDTH", 0.45, smooth);
}
par {
show(baseEdge, 0.18);
shift3(wire, (0,8,0), 2.80, smooth);
to(sheet, opacity, 0.14, 2.80, smooth);
view3(sheet, "isometric", 2.10, smooth, 1.52);
stagger(0.075) { for i in 1..8 { show(row{i}, 0.12); show(col{i}, 0.12); } }
stagger(0.075) { for i in 1..8 { draw(row{i}, 0.70, smooth); draw(col{i}, 0.70, smooth); } }
}
wait(0.65);
}
}
step("the flat world") {
seq {
say(narration, "The old wire is now one edge of an entire plane.", 0.50, smooth);
say(principle, "Forward, backward, left, and right — still no above.", 0.45, smooth);
pulse(sheetGrid, 0.75);
wait(0.80);
}
}
step("the rising room") {
seq {
par {
say(phase, "3D · THE RISING ROOM", 0.45, smooth);
say(narration, "The complete sheet catches a breath and lifts straight upward.", 0.55, smooth);
say(principle, "The moving surface fills the hollow space below it.", 0.50, smooth);
say(progress, "POINT → LENGTH → WIDTH → HEIGHT", 0.50, smooth);
}
par {
shift3(sheetSkin, (0,0,8), 2.85, smooth);
to(room, opacity, 0.16, 2.85, smooth);
show(pillars, 0.15);
draw(pillars, 2.70, smooth);
view3(room, "isometric", 2.85, smooth, 1.52);
}
wait(0.65);
}
}
step("the space within") {
seq {
par {
show(nearCorner, 0.30); show(farCorner, 0.30);
show(nearLabel, 0.35); show(farLabel, 0.35);
show(takeaway, 0.45);
say(phase, "THE SPACE WITHIN", 0.45, smooth);
say(narration, "Depth rushes in. Near and far finally have meaning.", 0.55, smooth);
say(principle, "A dimension is an old world moving in a new perpendicular direction.", 0.55, smooth);
view3(room, "fit", 1.80, smooth, 1.62);
}
par {
pulse(sheetSkin, 0.85);
pulse(pillars, 0.85);
roll3(3, 1.80, smooth);
}
wait(2.20);
}
}
textbook-length-area-volume
Why units become cm, cm², and cm³: one measured segment sweeps out a rectangle, then the rectangle rises into a cuboid. A textbook measurement story built from extrusion rather than three disconnected formulas.
// ============================================================================
// textbook-length-area-volume.manic — centimetres, square centimetres, cubes
// ============================================================================
title("Length, Area, Volume — Why the Units Change");
canvas("9:16");
template("blueprint");
watermark(manicMark, (220, 175), "Made With Manic");
creator(me, "@anish2good name=Manic_Math tagline=Textbooks_in_motion yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=cyan secondary=magenta footer=social safe=reels");
socials(me);
text(kicker, (540,164), "THE MEASUREMENT LADDER"); size(kicker,21); bold(kicker); color(kicker,cyan); hidden(kicker);
text(headline, (540,228), "Why units become squared, then cubed"); size(headline,36); bold(headline); hidden(headline);
text(phase, (540,300), "1D · MEASURE LENGTH"); size(phase,23); bold(phase); color(phase,gold); hidden(phase);
text(narration, (540,1328), "A ruler counts how many unit segments fit along one direction."); size(narration,26); bold(narration); wrap(narration,850); hidden(narration);
text(principle, (540,1388), "One direction · centimetres"); size(principle,20); color(principle,dim); hidden(principle);
equation(measure, (540,1480), `L=\textcolor{cyan}{6}\text{ cm}`, 40); hidden(measure);
text(takeaway, (540,1560), "THE UNIT CHANGES BECAUSE THE NUMBER OF DIRECTIONS CHANGES"); size(takeaway,18); bold(takeaway); color(takeaway,gold); hidden(takeaway);
// This is the settled `view3(length,"front",...,1.65)` composition: the
// opening line is already inside the Creator media region, so frame one moves
// the mathematics rather than jolting the camera into place.
camera3((0.26,-33.6,-4.14),(0.26,-2,-4.14),40);
line3(length, (-3,-2,0), (3,-2,0)); color(length,cyan); thick(length,0.07); untraced(length); tag(length,areaSkin);
point3(start, (-3,-2,0),0.15); color(start,fg); hidden(start);
point3(finish, (3,-2,0),0.15); color(finish,fg); hidden(finish);
point3(lengthMid, (0,-2,0),0.01); hidden(lengthMid);
text(lengthLabel,(0,0),"6 cm"); size(lengthLabel,22); bold(lengthLabel); color(lengthLabel,cyan); hidden(lengthLabel); pin3(lengthLabel,lengthMid);
// Six visible unit segments sit directly on the measured line.
for i in 0..6 {
line3(unit{i}, (-3+i,-2,0.10), (-2+i,-2,0.10));
color(unit{i},gold); thick(unit{i},0.035); untraced(unit{i}); hidden(unit{i}); tag(unit{i},units1d); tag(unit{i},areaSkin);
}
// The measured line sweeps four centimetres sideways to create 24 unit squares.
cube3(areaSheet, (0,0,0), (6,4,0.08)); color(areaSheet,blue); hidden(areaSheet); tag(areaSheet,areaSkin);
finish3(areaSheet,"material=glass shading=flat mesh=0.20 depth=0.25 shadow=0.10");
line3(areaBase,(-3,-2,0),(3,-2,0)); color(areaBase,cyan); thick(areaBase,0.05); hidden(areaBase); tag(areaBase,areaSkin);
for i in 1..4 {
let yy=-2+i;
line3(areaRow{i},(-3,yy,0.05),(3,yy,0.05)); color(areaRow{i},magenta); thick(areaRow{i},0.025); untraced(areaRow{i}); hidden(areaRow{i}); tag(areaRow{i},areaGrid); tag(areaRow{i},areaSkin);
}
for i in 0..7 {
let xx=-3+i;
line3(areaCol{i},(xx,-2,0.05),(xx,2,0.05)); color(areaCol{i},cyan); thick(areaCol{i},0.025); untraced(areaCol{i}); hidden(areaCol{i}); tag(areaCol{i},areaGrid); tag(areaCol{i},areaSkin);
}
point3(widthMid,(-3,0,0),0.01); hidden(widthMid);
text(widthLabel,(0,0),"4 cm"); size(widthLabel,22); bold(widthLabel); color(widthLabel,magenta); hidden(widthLabel); pin3(widthLabel,widthMid);
// The complete square-unit sheet rises three centimetres and leaves 72 cubes.
cube3(volumeBox,(0,0,1.5),(6,4,3)); color(volumeBox,blue); hidden(volumeBox);
finish3(volumeBox,"material=glass shading=flat mesh=0.48 depth=0.34 shadow=0.16");
line3(height0,(-3,-2,0),(-3,-2,3)); color(height0,lime); thick(height0,0.06); untraced(height0); hidden(height0); tag(height0,heightEdges);
line3(height1,(3,-2,0),(3,-2,3)); color(height1,lime); thick(height1,0.06); untraced(height1); hidden(height1); tag(height1,heightEdges);
line3(height2,(-3,2,0),(-3,2,3)); color(height2,lime); thick(height2,0.06); untraced(height2); hidden(height2); tag(height2,heightEdges);
line3(height3,(3,2,0),(3,2,3)); color(height3,lime); thick(height3,0.06); untraced(height3); hidden(height3); tag(height3,heightEdges);
point3(heightMid,(3,2,1.5),0.01); hidden(heightMid);
text(heightLabel,(0,0),"3 cm"); size(heightLabel,22); bold(heightLabel); color(heightLabel,lime); hidden(heightLabel); pin3(heightLabel,heightMid);
step("count one direction") {
seq {
par { show(kicker,0.35); show(headline,0.55); show(phase,0.40); show(narration,0.45); show(principle,0.40); show(measure,0.50); }
par { show(start,0.25); show(finish,0.25); draw(length,1.25,smooth); show(lengthLabel,0.35); }
par {
stagger(0.10) { for i in 0..6 { show(unit{i},0.10); } }
stagger(0.10) { for i in 0..6 { draw(unit{i},0.35,smooth); } }
}
wait(0.60);
}
}
step("sweep out area") {
seq {
par {
say(phase,"2D · COUNT UNIT SQUARES",0.45,smooth);
say(narration,"Move the six-centimetre length through four new centimetres.",0.55,smooth);
say(principle,"Two perpendicular directions · square centimetres",0.45,smooth);
rewrite(measure,`A=\textcolor{cyan}{6}\times\textcolor{magenta}{4}=\textcolor{gold}{24}\text{ cm}^2`,1.10,smooth);
fade(start,0.30); fade(finish,0.30);
view3(areaSheet,"isometric",1.25,smooth,1.48);
}
par {
shift3(units1d,(0,4,0),2.60,smooth);
shift3(length,(0,4,0),2.60,smooth);
show(areaBase,0.15);
to(areaSheet,opacity,0.15,2.60,smooth);
show(widthLabel,0.35);
stagger(0.07) { for i in 1..4 { show(areaRow{i},0.10); } for i in 0..7 { show(areaCol{i},0.10); } }
stagger(0.07) { for i in 1..4 { draw(areaRow{i},0.55,smooth); } for i in 0..7 { draw(areaCol{i},0.55,smooth); } }
}
wait(0.70);
}
}
step("see twenty four squares") {
seq {
say(narration,"Six columns by four rows: the floor contains 24 unit squares.",0.50,smooth);
par { pulse(areaGrid,0.75); pulse(areaSheet,0.75); }
wait(0.75);
}
}
step("stack into volume") {
seq {
par {
say(phase,"3D · STACK UNIT CUBES",0.45,smooth);
say(narration,"Lift the complete 24-square layer through three centimetres.",0.55,smooth);
say(principle,"Three perpendicular directions · cubic centimetres",0.45,smooth);
rewrite(measure,`V=\textcolor{gold}{24}\times\textcolor{lime}{3}=\textcolor{gold}{72}\text{ cm}^3`,1.10,smooth);
fade(lengthLabel,0.30); fade(widthLabel,0.30);
}
par {
shift3(areaSkin,(0,0,3),2.70,smooth);
to(volumeBox,opacity,0.17,2.70,smooth);
show(heightEdges,0.15); draw(heightEdges,2.55,smooth);
show(heightLabel,0.35);
view3(volumeBox,"isometric",2.70,smooth,1.50);
}
wait(0.70);
}
}
step("the measurement ladder") {
seq {
par {
show(takeaway,0.45);
say(phase,"LENGTH → AREA → VOLUME",0.50,smooth);
say(narration,"The number is not merely larger. The kind of unit has changed.",0.55,smooth);
say(principle,"cm measures a path · cm² tiles a surface · cm³ fills space",0.55,smooth);
view3(volumeBox,"fit",1.80,smooth,1.58);
}
par { pulse(volumeBox,0.85); pulse(heightEdges,0.85); roll3(3,1.60,smooth); }
wait(2.10);
}
}
textbook-coordinate-worlds
A point earns a longer address as dimensions unlock: x on a line, (x,y) on a plane, then (x,y,z) in space. Coordinates remain attached to the same idea while the world expands around it.
// ============================================================================
// textbook-coordinate-worlds.manic — number line → coordinate plane → space
// ============================================================================
title("A Point Gets an Address — From 1D to 3D");
canvas("9:16");
template("blueprint");
watermark(manicMark, (220, 175), "Made With Manic");
creator(me, "@anish2good name=Manic_Math tagline=Textbooks_in_motion yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=cyan secondary=magenta footer=social safe=reels");
socials(me);
text(kicker,(540,164),"COORDINATE GEOMETRY"); size(kicker,21); bold(kicker); color(kicker,cyan); hidden(kicker);
text(headline,(540,228),"A point gains one coordinate at a time"); size(headline,32); bold(headline); hidden(headline);
text(phase,(540,300),"1D · THE NUMBER LINE"); size(phase,23); bold(phase); color(phase,gold); hidden(phase);
text(narration,(540,1328),"One direction needs one number: move three units along x."); size(narration,26); bold(narration); wrap(narration,850); hidden(narration);
text(principle,(540,1392),"Every new perpendicular direction adds one coordinate."); size(principle,20); color(principle,dim); hidden(principle);
equation(address,(540,1490),`P=\textcolor{cyan}{(3)}`,44); hidden(address);
text(takeaway,(540,1572),"THE POINT STAYS THE SAME · ITS ADDRESS BECOMES MORE PRECISE"); size(takeaway,18); bold(takeaway); color(takeaway,gold); hidden(takeaway);
camera3((0,0,14),(0,0,0),38);
// One persistent point begins at the origin, then gains y and z coordinates.
point3(P,(0,0,0),0.18); color(P,gold); hidden(P);
text(pLabel,(0,0),"P"); size(pLabel,34); bold(pLabel); color(pLabel,gold); hidden(pLabel); label3(pLabel,P,0.34);
point3(origin,(0,0,0),0.10); color(origin,fg); hidden(origin);
text(originLabel,(0,0),"O"); size(originLabel,34); color(originLabel,dim); hidden(originLabel); label3(originLabel,origin,0.36);
line3(xAxis,(-4,0,0),(4,0,0)); color(xAxis,cyan); thick(xAxis,0.045); untraced(xAxis); hidden(xAxis); tag(xAxis,axes);
line3(yAxis,(0,-3,0),(0,3,0)); color(yAxis,magenta); thick(yAxis,0.045); untraced(yAxis); hidden(yAxis); tag(yAxis,axes);
line3(zAxis,(0,0,0),(0,0,4)); color(zAxis,lime); thick(zAxis,0.045); untraced(zAxis); hidden(zAxis); tag(zAxis,axes);
point3(xEnd,(4,0,0),0.01); hidden(xEnd);
point3(yEnd,(0,3,0),0.01); hidden(yEnd);
point3(zEnd,(0,0,4),0.01); hidden(zEnd);
text(xLabel,(0,0),"x"); size(xLabel,34); bold(xLabel); color(xLabel,cyan); hidden(xLabel); label3(xLabel,xEnd,0.38);
text(yLabel,(0,0),"y"); size(yLabel,34); bold(yLabel); color(yLabel,magenta); hidden(yLabel); label3(yLabel,yEnd,0.38);
text(zLabel,(0,0),"z"); size(zLabel,34); bold(zLabel); color(zLabel,lime); hidden(zLabel); label3(zLabel,zEnd,0.38);
// Tick marks remain quiet so the address, rather than decoration, leads.
for i in -4..5 {
line3(xTick{i},(i,-0.10,0),(i,0.10,0)); color(xTick{i},cyan); thick(xTick{i},0.025); untraced(xTick{i}); hidden(xTick{i}); tag(xTick{i},xTicks);
}
for i in -3..4 {
line3(yTick{i},(-0.10,i,0),(0.10,i,0)); color(yTick{i},magenta); thick(yTick{i},0.025); untraced(yTick{i}); hidden(yTick{i}); tag(yTick{i},yTicks);
}
for i in 1..5 {
line3(zTick{i},(-0.10,0,i),(0.10,0,i)); color(zTick{i},lime); thick(zTick{i},0.025); untraced(zTick{i}); hidden(zTick{i}); tag(zTick{i},zTicks);
}
grid3(plane,(0,0,0),4,1); color(plane,blue); opacity(plane,0.22); hidden(plane); tag(plane,world2d);
cube3(space,(0,0,2),(8,6,4)); color(space,blue); hidden(space); tag(space,world3d);
finish3(space,"material=glass shading=flat mesh=0.18 depth=0.22 shadow=0.10");
// Guides expose how each coordinate is read from the axes.
line3(xGuide,(0,0,0),(3,0,0)); color(xGuide,cyan); thick(xGuide,0.065); untraced(xGuide); hidden(xGuide); tag(xGuide,guides);
line3(yGuide,(3,0,0),(3,2,0)); color(yGuide,magenta); thick(yGuide,0.065); untraced(yGuide); hidden(yGuide); tag(yGuide,guides);
line3(zGuide,(3,2,0),(3,2,3)); color(zGuide,lime); thick(zGuide,0.065); untraced(zGuide); hidden(zGuide); tag(zGuide,guides);
point3(xFoot,(3,0,0),0.11); color(xFoot,cyan); hidden(xFoot);
point3(xyFoot,(3,2,0),0.11); color(xyFoot,magenta); hidden(xyFoot);
project3(shadow,P,"xy"); color(shadow,magenta); hidden(shadow);
link3(drop,P,shadow,0.12); color(drop,lime); thick(drop,0.025); hidden(drop);
step("one number locates the point") {
seq {
par { show(kicker,0.35); show(headline,0.55); show(phase,0.40); show(narration,0.45); show(principle,0.40); show(address,0.50); view3(xAxis,"top",0.9,smooth,1.65); }
par { show(origin,0.20); show(originLabel,0.25); show(P,0.25); show(pLabel,0.25); draw(xAxis,1.10,smooth); show(xLabel,0.25); show(xTicks,0.35); }
par { shift3(P,(3,0,0),1.65,smooth); draw(xGuide,1.65,smooth); }
par { show(xFoot,0.25); pulse(P,0.65); pulse(address,0.65); }
wait(0.60);
}
}
step("the plane adds a second direction") {
seq {
par {
say(phase,"2D · THE COORDINATE PLANE",0.45,smooth);
say(narration,"Open a perpendicular y-direction, then move two units sideways.",0.55,smooth);
rewrite(address,`P=\textcolor{cyan}{(3},\textcolor{magenta}{2)}`,0.95,smooth);
show(plane,0.80); draw(yAxis,1.35,smooth); show(yLabel,0.30); show(yTicks,0.40);
view3(plane,"top",1.55,smooth,1.55);
}
par { shift3(P,(0,2,0),1.75,smooth); draw(yGuide,1.75,smooth); }
par { show(xyFoot,0.25); pulse(guides,0.80); pulse(address,0.80); }
wait(0.70);
}
}
step("read the ordered pair") {
seq {
say(narration,"First read x, then y. Order matters: (3,2) is not (2,3).",0.50,smooth);
par { pulse(xGuide,0.60); pulse(yGuide,0.60); }
wait(0.85);
}
}
step("space adds height") {
seq {
par {
say(phase,"3D · COORDINATE SPACE",0.45,smooth);
say(narration,"Raise a z-axis out of the plane and lift the point three units.",0.55,smooth);
rewrite(address,`P=\textcolor{cyan}{(3},\textcolor{magenta}{2},\textcolor{lime}{3)}`,1.05,smooth);
to(space,opacity,0.14,2.40,smooth);
draw(zAxis,1.55,smooth); show(zLabel,0.30); show(zTicks,0.40);
view3(space,"isometric",2.35,smooth,1.48);
}
par {
shift3(P,(0,0,3),2.15,smooth);
draw(zGuide,2.15,smooth);
show(shadow,0.30); show(drop,0.30);
}
par { pulse(P,0.70); pulse(address,0.70); }
wait(0.70);
}
}
step("one point three coordinates") {
seq {
par {
show(takeaway,0.45);
say(phase,"ONE ADDRESS · THREE DIRECTIONS",0.50,smooth);
say(narration,"x says across, y says sideways, and z says how high.",0.55,smooth);
say(principle,"1D: (x) · 2D: (x,y) · 3D: (x,y,z)",0.55,smooth);
view3(space,"fit",1.70,smooth,1.55);
}
par { pulse(axes,0.90); pulse(P,0.90); roll3(3,1.55,smooth); }
wait(1.80);
}
}
textbook-function-to-solid
A diameter becomes a semicircle and the semicircle revolves into a sphere. The story links a 1D domain, a 2D graph, and a 3D solid through one continuous generating motion.
// ============================================================================
// textbook-function-to-solid.manic — interval → graph → solid of revolution
// ============================================================================
title("The Revolving Semicircle — From 1D to 3D");
canvas("9:16");
template("neon");
watermark(manicMark,(220,175),"Made With Manic");
creator(me,"@anish2good name=Manic_Math tagline=Textbooks_in_motion yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=cyan secondary=magenta footer=social safe=reels");
socials(me);
text(kicker,(540,164),"SOLIDS OF REVOLUTION"); size(kicker,21); bold(kicker); color(kicker,cyan); hidden(kicker);
text(headline,(540,228),"A 1D domain grows into a 3D sphere"); size(headline,41); bold(headline); hidden(headline);
text(phase,(540,300),"1D · CHOOSE THE DOMAIN"); size(phase,23); bold(phase); color(phase,gold); hidden(phase);
text(narration,(540,1328),"Begin with every x-value from −2 to 2 on a number line."); size(narration,26); bold(narration); wrap(narration,850); hidden(narration);
text(principle,(540,1392),"The interval controls where the graph exists."); size(principle,20); color(principle,dim); hidden(principle);
equation(formula,(540,1488),`x\in\textcolor{cyan}{[-2,2]}`,44); hidden(formula);
text(takeaway,(540,1572),"DOMAIN → PROFILE → ROTATION → VOLUME"); size(takeaway,18); bold(takeaway); color(takeaway,gold); hidden(takeaway);
camera3((0,-14,1.8),(0,0,0.7),39);
// The x-axis and interval are the 1D seed for the whole construction.
line3(xAxis,(-2.6,0,0),(2.6,0,0)); color(xAxis,dim); thick(xAxis,0.035); untraced(xAxis); hidden(xAxis);
line3(domain,(-2,0,0),(2,0,0)); color(domain,cyan); thick(domain,0.09); untraced(domain); hidden(domain); tag(domain,profile);
point3(leftEnd,(-2,0,0),0.13); color(leftEnd,cyan); hidden(leftEnd);
point3(rightEnd,(2,0,0),0.13); color(rightEnd,cyan); hidden(rightEnd);
point3(origin,(0,0,0),0.09); color(origin,fg); hidden(origin);
point3(xEnd,(2.6,0,0),0.01); hidden(xEnd);
text(xLabel,(0,0),"x"); size(xLabel,23); bold(xLabel); color(xLabel,fg); hidden(xLabel); label3(xLabel,xEnd,0.30);
// The upper semicircle is a textbook profile: y = sqrt(4 - x^2).
curve3(semicircle,"-2+4*t","0","sqrt(4-(-2+4*t)^2)",(0,1)); color(semicircle,magenta); thick(semicircle,0.065); untraced(semicircle); hidden(semicircle); tag(semicircle,profile);
param3(region,
"-2+4*u",
"0",
"v*sqrt(4-(-2+4*u)^2)",
(0,1),(0,1),32);
color(region,magenta); hidden(region); tag(region,profile);
finish3(region,"material=glass shading=flat mesh=0.18 depth=0.18 shadow=0.08");
// Three sample heights explain how the graph rises from the domain.
line3(heightA,(-1,0,0),(-1,0,1.732)); color(heightA,gold); thick(heightA,0.035); untraced(heightA); hidden(heightA); tag(heightA,samples); tag(heightA,profile);
line3(heightB,(0,0,0),(0,0,2)); color(heightB,gold); thick(heightB,0.035); untraced(heightB); hidden(heightB); tag(heightB,samples); tag(heightB,profile);
line3(heightC,(1,0,0),(1,0,1.732)); color(heightC,gold); thick(heightC,0.035); untraced(heightC); hidden(heightC); tag(heightC,samples); tag(heightC,profile);
point3(apex,(0,0,2),0.10); color(apex,gold); hidden(apex); tag(apex,profile);
// Rotating the filled profile around x generates a radius-2 sphere.
param3(sphere,
"2*cos(u)",
"2*sin(u)*cos(v)",
"2*sin(u)*sin(v)",
(0,3.1416),(0,6.2832),38);
color(sphere,blue); hidden(sphere); tag(sphere,solid);
finish3(sphere,"material=glass shading=smooth mesh=0.24 depth=0.34 shadow=0.16");
curve3(equator,"0","2*cos(t)","2*sin(t)",(0,6.2832)); color(equator,cyan); thick(equator,0.035); untraced(equator); hidden(equator); tag(equator,solid);
curve3(meridian,"2*cos(t)","0","2*sin(t)",(0,6.2832)); color(meridian,magenta); thick(meridian,0.035); untraced(meridian); hidden(meridian); tag(meridian,solid);
step("mark the one dimensional domain") {
seq {
par { show(kicker,0.35); show(headline,0.55); show(phase,0.40); show(narration,0.45); show(principle,0.40); show(formula,0.50); view3(domain,"front",0.9,smooth,1.72); }
par { draw(xAxis,0.90,smooth); draw(domain,1.35,smooth); show(leftEnd,0.25); show(rightEnd,0.25); show(origin,0.25); show(xLabel,0.25); }
par { pulse(domain,0.75); pulse(formula,0.75); }
wait(0.65);
}
}
step("the function supplies height") {
seq {
par {
say(phase,"2D · DRAW THE PROFILE",0.45,smooth);
say(narration,"At each x, the function supplies a height above the axis.",0.55,smooth);
say(principle,"The interval becomes the base of an upper semicircle.",0.45,smooth);
rewrite(formula,`y=\textcolor{magenta}{\sqrt{4-x^2}},\quad -2\le x\le2`,1.05,smooth);
draw(semicircle,2.10,smooth);
stagger(0.22) { show(heightA,0.20); show(heightB,0.20); show(heightC,0.20); }
stagger(0.22) { draw(heightA,0.65,smooth); draw(heightB,0.65,smooth); draw(heightC,0.65,smooth); }
}
par { show(apex,0.25); pulse(samples,0.75); }
wait(0.70);
}
}
step("fill the generating region") {
seq {
par {
say(narration,"The curve and the x-axis enclose the region that will rotate.",0.50,smooth);
to(region,opacity,0.26,1.65,smooth);
view3(region,"front",1.40,smooth,1.62);
}
par { pulse(region,0.75); pulse(semicircle,0.75); }
wait(0.75);
}
}
step("rotate the profile into space") {
seq {
par {
say(phase,"3D · ROTATE ABOUT THE x-AXIS",0.45,smooth);
say(narration,"Sweep the entire semicircle through 360° around its diameter.",0.55,smooth);
say(principle,"Every point traces a circle; together those circles form a sphere.",0.50,smooth);
rewrite(formula,`x^2+y^2+z^2=\textcolor{gold}{4}`,1.05,smooth);
turn3(profile,(0,0,0),x,360,3.10,smooth);
to(sphere,opacity,0.34,3.10,smooth);
show(equator,0.45); draw(equator,2.15,smooth);
show(meridian,0.45); draw(meridian,2.15,smooth);
view3(solid,"isometric",2.60,smooth,1.55);
}
par { pulse(solid,0.85); roll3(4,1.50,smooth); }
wait(0.75);
}
}
step("read the solid of revolution") {
seq {
par {
show(takeaway,0.45);
say(phase,"THE SAME RADIUS · A NEW DIMENSION",0.50,smooth);
say(narration,"The 2D radius is still 2, but rotation has filled 3D volume.",0.55,smooth);
say(principle,"A semicircle rotated about its diameter generates a sphere.",0.50,smooth);
rewrite(formula,`V=\frac{4}{3}\pi(2)^3=\textcolor{gold}{\frac{32\pi}{3}}`,1.15,smooth);
view3(solid,"fit",1.65,smooth,1.58);
}
par { pulse(sphere,0.90); pulse(formula,0.90); roll3(3,1.50,smooth); }
wait(1.90);
}
}
textbook-statistical-dimensions
Data grows from a one-variable number line to a two-variable scatter plot and a three-variable point cloud. The axes and observations evolve together so statistical dimension reads as information, not decoration.
// ============================================================================
// textbook-statistical-dimensions.manic
// One variable → two variables → three variables, using the same 12 students.
// Stats kit: summary + correlation. 3D primitives reveal the third variable.
// ============================================================================
title("Statistical Dimensions — From a List to a Data Cloud");
canvas("9:16");
template("blueprint");
watermark(manicMark,(220,175),"Made With Manic");
creator(me,"@anish2good name=Manic_Stats tagline=Textbooks_in_motion yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=cyan secondary=magenta footer=social safe=reels");
socials(me);
text(kicker,(540,164),"MULTIVARIATE STATISTICS"); size(kicker,21); bold(kicker); color(kicker,cyan); hidden(kicker);
text(headline,(540,228),"The same students, in more dimensions"); size(headline,33); bold(headline); hidden(headline);
text(phase,(540,300),"1D · ONE VARIABLE"); size(phase,23); bold(phase); color(phase,gold); hidden(phase);
text(narration,(540,1328),"Record only height. Each student needs one number."); size(narration,26); bold(narration); wrap(narration,850); hidden(narration);
text(principle,(540,1392),"Centre and spread describe one-dimensional data."); size(principle,20); color(principle,dim); hidden(principle);
equation(address,(540,1488),`\text{student}=\textcolor{cyan}{(\text{height})}`,39); hidden(address);
text(takeaway,(540,1572),"MORE VARIABLES DO NOT ADD PEOPLE · THEY ADD STRUCTURE"); size(takeaway,18); bold(takeaway); color(takeaway,gold); hidden(takeaway);
let vx=518;
let vy=610;
// Twelve students, measured in centimetres.
summary(one,(vx,vy),
"150 154 158 160 164 167 170 173 176 180 184 188",760,cyan);
untraced(one.line); hidden(one.dots); hidden(one.band);
hidden(one.meanmark); hidden(one.meanlbl);
hidden(one.medianmark); hidden(one.medianlbl);
hidden(one.readout); hidden(one.min); hidden(one.max);
// The same students: height and arm span. Target points remain hidden because
// the 1D dots will `become` these blueprints, preserving one identity per row.
correlation(two,(vx,vy),10,
"150 151 154 153 158 160 160 159 164 165 167 168 170 169 173 175 176 174 180 181 184 185 188 187",cyan);
hidden(two.points); untraced(two.line); hidden(two.r);
// Front view: x = height, screen-up z = arm span. Depth y stores shoe size.
// The camera begins in the exact 2D projection; its later isometric move is
// what makes the third variable perceptible.
camera3((0.53,-64.4,-5.34),(0.53,0,-5.34),40);
cube3(dataRoom,(0,0,3.1),(7.2,5.6,6.2)); color(dataRoom,blue); hidden(dataRoom); tag(dataRoom,multivariate);
finish3(dataRoom,"material=glass shading=flat mesh=0.17 depth=0.22 shadow=0.10");
line3(heightAxis,(-3.6,-2.8,0),(3.6,-2.8,0)); color(heightAxis,cyan); thick(heightAxis,0.045); untraced(heightAxis); hidden(heightAxis); tag(heightAxis,multivariate); tag(heightAxis,axes3d);
line3(shoeAxis,(-3.6,-2.8,0),(-3.6,2.8,0)); color(shoeAxis,magenta); thick(shoeAxis,0.045); untraced(shoeAxis); hidden(shoeAxis); tag(shoeAxis,multivariate); tag(shoeAxis,axes3d);
line3(armAxis,(-3.6,-2.8,0),(-3.6,-2.8,6.2)); color(armAxis,lime); thick(armAxis,0.045); untraced(armAxis); hidden(armAxis); tag(armAxis,multivariate); tag(armAxis,axes3d);
point3(heightEnd,(3.6,-2.8,0),0.01); hidden(heightEnd);
point3(shoeEnd,(-3.6,2.8,0),0.01); hidden(shoeEnd);
point3(armEnd,(-3.6,-2.8,6.2),0.01); hidden(armEnd);
text(heightLabel,(0,0),"height"); size(heightLabel,25); bold(heightLabel); color(heightLabel,cyan); hidden(heightLabel); label3(heightLabel,heightEnd,0.40);
text(shoeLabel,(0,0),"shoe size"); size(shoeLabel,25); bold(shoeLabel); color(shoeLabel,magenta); hidden(shoeLabel); label3(shoeLabel,shoeEnd,0.40);
text(armLabel,(0,0),"arm span"); size(armLabel,25); bold(armLabel); color(armLabel,lime); hidden(armLabel); label3(armLabel,armEnd,0.40);
// Normalised triples: (height, shoe-size depth, arm-span height).
point3(p0,(-3.167,-2.4,0.000),0.13); color(p0,cyan); hidden(p0); tag(p0,cloud); tag(p0,multivariate);
point3(p1,(-2.500,-1.6,0.333),0.13); color(p1,cyan); hidden(p1); tag(p1,cloud); tag(p1,multivariate);
point3(p2,(-1.833,-1.6,1.500),0.13); color(p2,cyan); hidden(p2); tag(p2,cloud); tag(p2,multivariate);
point3(p3,(-1.500,-0.8,1.333),0.13); color(p3,cyan); hidden(p3); tag(p3,cloud); tag(p3,multivariate);
point3(p4,(-0.833,-0.8,2.333),0.13); color(p4,cyan); hidden(p4); tag(p4,cloud); tag(p4,multivariate);
point3(p5,(-0.333, 0.0,2.833),0.13); color(p5,cyan); hidden(p5); tag(p5,cloud); tag(p5,multivariate);
point3(p6,( 0.167, 0.0,3.000),0.13); color(p6,cyan); hidden(p6); tag(p6,cloud); tag(p6,multivariate);
point3(p7,( 0.667, 0.8,4.000),0.16); color(p7,gold); hidden(p7); tag(p7,cloud); tag(p7,multivariate);
point3(p8,( 1.167, 0.8,3.833),0.13); color(p8,cyan); hidden(p8); tag(p8,cloud); tag(p8,multivariate);
point3(p9,( 1.833, 1.6,5.000),0.13); color(p9,cyan); hidden(p9); tag(p9,cloud); tag(p9,multivariate);
point3(p10,(2.500, 1.6,5.667),0.13); color(p10,cyan); hidden(p10); tag(p10,cloud); tag(p10,multivariate);
point3(p11,(3.167, 2.4,6.000),0.13); color(p11,cyan); hidden(p11); tag(p11,cloud); tag(p11,multivariate);
project3(frontShadow,p7,"xz"); color(frontShadow,gold); hidden(frontShadow); tag(frontShadow,multivariate);
link3(depthLink,p7,frontShadow,0.14); color(depthLink,magenta); thick(depthLink,0.025); hidden(depthLink); tag(depthLink,multivariate);
text(depthNote,(0,0),"shoe size adds depth"); size(depthNote,24); bold(depthNote); color(depthNote,gold); hidden(depthNote); label3(depthNote,p7,0.42);
step("one variable centre and spread") {
seq {
par { show(kicker,0.35); show(headline,0.55); show(phase,0.40); show(narration,0.45); show(principle,0.40); show(address,0.55); }
draw(one.line,0.75,smooth);
par { show(one.min,0.25); show(one.max,0.25); stagger(0.09) { for i in 0..12 { show(one.dot{i},0.18); } } }
par { to(one.band,opacity,0.12,0.65,smooth); show(one.meanmark,0.35); show(one.meanlbl,0.35); show(one.medianmark,0.35); show(one.medianlbl,0.35); }
par { show(one.readout,0.35); pulse(one.dots,0.75); }
wait(0.70);
}
}
step("a second variable reveals relationship") {
seq {
par {
say(phase,"2D · TWO VARIABLES",0.45,smooth);
say(narration,"Add arm span. Every student now becomes an ordered pair.",0.55,smooth);
say(principle,"The slope of the cloud reveals how two measurements move together.",0.50,smooth);
rewrite(address,`\text{student}=\textcolor{cyan}{(\text{height},\text{arm span})}`,1.00,smooth);
fade(one.band,0.45); fade(one.meanmark,0.45); fade(one.meanlbl,0.45);
fade(one.medianmark,0.45); fade(one.medianlbl,0.45); fade(one.readout,0.45);
fade(one.line,0.45); fade(one.min,0.35); fade(one.max,0.35);
}
par {
for i in 0..12 { become(one.dot{i},two.p{i},1.75,smooth); }
}
par { draw(two.line,1.05,smooth); show(two.r,0.45); }
par { pulse(one.dots,0.75); pulse(two.r,0.75); }
wait(0.70);
}
}
step("the front projection still looks two dimensional") {
seq {
say(narration,"A front view can hide depth: the 3D records still resemble the scatterplot.",0.55,smooth);
par {
fade(one.dots,0.80);
fade(two.line,0.80);
fade(two.r,0.80);
stagger(0.08) { for i in 0..12 { show(p{i},0.18); } }
show(heightAxis,0.35); show(armAxis,0.35);
draw(heightAxis,1.10,smooth); draw(armAxis,1.10,smooth);
show(heightLabel,0.30); show(armLabel,0.30);
}
wait(0.75);
}
}
step("rotate to reveal the third variable") {
seq {
par {
say(phase,"3D · THREE VARIABLES",0.45,smooth);
say(narration,"Rotate the view. Shoe size reveals variation the flat plot could not show.",0.55,smooth);
say(principle,"A third coordinate turns a scatterplot into a multivariate cloud.",0.50,smooth);
rewrite(address,`\text{student}=\textcolor{cyan}{(\text{height},\text{arm span},\text{shoe})}`,1.10,smooth);
to(dataRoom,opacity,0.13,2.50,smooth);
show(shoeAxis,0.30); draw(shoeAxis,1.20,smooth); show(shoeLabel,0.30);
view3(multivariate,"isometric",2.65,smooth,1.52);
}
par { show(frontShadow,0.30); show(depthLink,0.35); show(depthNote,0.35); pulse(p7,0.75); }
wait(0.75);
}
}
step("dimensions describe not duplicate") {
seq {
par {
show(takeaway,0.45);
say(phase,"12 STUDENTS · 36 MEASUREMENTS",0.50,smooth);
say(narration,"The sample size stayed twelve; each observation simply gained context.",0.55,smooth);
say(principle,"1D describes · 2D relates · 3D separates hidden structure",0.55,smooth);
view3(multivariate,"fit",1.60,smooth,1.58);
}
par { pulse(cloud,0.90); pulse(address,0.90); roll3(3,1.50,smooth); }
wait(1.90);
}
}
textbook-geometry-dimension-reduction
The reverse journey: a sphere reveals a great-circle section, then that circle collapses to its diameter. A 3D→2D→1D geometry lesson that makes section and projection relationships explicit.
// ============================================================================
// textbook-geometry-dimension-reduction.manic
// Sphere → central cross-section → circle → diameter (3D → 2D → 1D).
// Geo kit: point, circle2, linecircle, segment, midpoint.
// ============================================================================
title("Dimension Reduction — From Sphere to Diameter");
canvas("9:16");
template("blueprint");
watermark(manicMark,(220,175),"Made With Manic");
creator(me,"@anish2good name=Manic_Geometry tagline=Textbooks_in_motion yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=cyan secondary=magenta footer=social safe=reels");
socials(me);
text(kicker,(540,164),"GEOMETRY BY DIMENSION"); size(kicker,21); bold(kicker); color(kicker,cyan); hidden(kicker);
text(headline,(540,228),"A sphere hides a circle — and a line"); size(headline,36); bold(headline); hidden(headline);
text(phase,(540,300),"3D · BEGIN WITH A SPHERE"); size(phase,23); bold(phase); color(phase,gold); hidden(phase);
text(narration,(540,1328),"A sphere extends in three independent directions."); size(narration,26); bold(narration); wrap(narration,850); hidden(narration);
text(principle,(540,1392),"Its radius is 3 units in every direction."); size(principle,20); color(principle,dim); hidden(principle);
equation(formula,(540,1488),`x^2+y^2+z^2=\textcolor{gold}{3^2}`,43); hidden(formula);
text(takeaway,(540,1572),"FIX A COORDINATE · LOSE A DIMENSION · KEEP THE MEASURE"); size(takeaway,18); bold(takeaway); color(takeaway,gold); hidden(takeaway);
// Settled isometric composition of the complete sphere inside the Creator
// media region. Starting here prevents an opening camera correction.
camera3((40.05,-39.35,23.41),(3.15,-2.45,-6.79),40);
sphere3(solid,(0,0,0),3); color(solid,blue); hidden(solid); tag(solid,spatial);
finish3(solid,"material=glass shading=smooth mesh=0.25 depth=0.36 shadow=0.16");
// A thin xz-plane begins outside the sphere and travels to y=0.
cube3(slicePlane,(0,-5,0),(7,0.08,7)); color(slicePlane,magenta); hidden(slicePlane); tag(slicePlane,spatial);
finish3(slicePlane,"material=glass shading=flat mesh=0.10 depth=0.18 shadow=0.06");
curve3(greatCircle,"3*cos(t)","0","3*sin(t)",(0,6.2832)); color(greatCircle,magenta); thick(greatCircle,0.065); untraced(greatCircle); hidden(greatCircle); tag(greatCircle,spatial);
// Screen-space geo construction aligned with the settled front projection.
let gx=518;
let gy=890;
let gr=145;
point(O,(gx,gy),"O"); hidden(O); hidden(O.label);
point(A,(gx+gr,gy),"R"); hidden(A); hidden(A.label);
circle2(section,O,A); color(section,magenta); stroke(section,4); untraced(section);
segment(radius2d,O,A); color(radius2d,gold); stroke(radius2d,4); untraced(radius2d);
// Two hidden points define a horizontal secant. The geo kit computes its exact
// circle intersections `ends0` and `ends1`, then derives their midpoint M.
point(lineLeft,(gx-gr-55,gy)); hidden(lineLeft);
point(lineRight,(gx+gr+55,gy)); hidden(lineRight);
linecircle(ends,lineLeft,lineRight,O,A);
hidden(ends0); hidden(ends1);
label(ends0,"A",(-18,-22)); hidden(ends0.label);
label(ends1,"B",(18,-22)); hidden(ends1.label);
segment(diameter,ends0,ends1); color(diameter,cyan); stroke(diameter,6); untraced(diameter);
midpoint(M,ends0,ends1); color(M,lime); hidden(M);
label(M,"M",(0,-24)); hidden(M.label);
step("the full three dimensional object") {
seq {
par { show(kicker,0.35); show(headline,0.55); show(phase,0.40); show(narration,0.45); show(principle,0.40); show(formula,0.55); }
show(solid,0.85);
par { pulse(solid,0.85); roll3(4,1.45,smooth); }
wait(0.70);
}
}
step("fix one coordinate with a plane") {
seq {
par {
say(phase,"3D → 2D · TAKE A CENTRAL SLICE",0.45,smooth);
say(narration,"Move a plane to the centre and fix y=0.",0.55,smooth);
say(principle,"Only points shared by the sphere and plane survive.",0.50,smooth);
rewrite(formula,`y=0\quad\Longrightarrow\quad x^2+z^2=\textcolor{gold}{3^2}`,1.05,smooth);
show(slicePlane,0.35);
}
par {
shift3(slicePlane,(0,5,0),2.10,smooth);
show(greatCircle,0.25); draw(greatCircle,2.10,smooth);
}
par { pulse(greatCircle,0.75); to(solid,opacity,0.04,0.65,smooth); fade(slicePlane,0.65); }
// A generous margin makes the spatial circle settle at the same on-screen
// radius as the following geo-kit circle, avoiding a dimensional size jump.
view3(greatCircle,"front",1.65,smooth,3.10);
wait(0.60);
}
}
step("the cross section becomes a geo circle") {
seq {
par {
say(phase,"2D · THE GREAT CIRCLE",0.45,smooth);
say(narration,"The surviving cross-section is a circle of radius 3.",0.55,smooth);
say(principle,"The geo kit binds the circle to centre O and radius point R.",0.50,smooth);
rewrite(formula,`x^2+z^2=9\quad\Longleftrightarrow\quad r=\textcolor{gold}{3}`,1.00,smooth);
fade(greatCircle,0.85);
draw(section,1.30,smooth);
}
par { show(O,0.25); show(O.label,0.25); show(A,0.25); show(A.label,0.25); draw(radius2d,0.85,smooth); }
par { pulse(section,0.75); pulse(radius2d,0.75); }
wait(0.70);
}
}
step("a line finds the diameter") {
seq {
par {
say(phase,"2D → 1D · INTERSECT WITH A LINE",0.45,smooth);
say(narration,"A line through the centre meets the circle at exactly two points.",0.55,smooth);
say(principle,"The constructed segment AB is the circle's widest chord.",0.50,smooth);
rewrite(formula,`AB=2r=2(3)`,0.95,smooth);
}
par { show(ends0,0.25); show(ends1,0.25); show(ends0.label,0.25); show(ends1.label,0.25); draw(diameter,1.35,smooth); }
par { fade(radius2d,0.45); fade(A,0.35); fade(A.label,0.35); show(M,0.30); show(M.label,0.30); pulse(M,0.70); }
wait(0.70);
}
}
step("only one measurable direction remains") {
seq {
par {
show(takeaway,0.45);
say(phase,"1D · THE DIAMETER",0.50,smooth);
say(narration,"Remove the surrounding circle. One segment—and one length—remains.",0.55,smooth);
say(principle,"3D sphere → 2D section → 1D diameter",0.55,smooth);
rewrite(formula,`\boxed{AB=\textcolor{gold}{6}\text{ units}}`,1.05,smooth);
fade(section,0.75); fade(O,0.40); fade(O.label,0.40);
}
par { pulse(diameter,0.90); pulse(formula,0.90); }
wait(2.00);
}
}
textbook-watermelon-sections
A paper-style spatial lesson that turns sphere sections into a continuous story: horizontal and vertical great-circle cuts make two halves, then perpendicular cuts separate one quarter from the three-quarter remainder. The section faces are bounded parametric surfaces, so the authored geometry is exact rather than a flat overlay.
// ============================================================================
// textbook-watermelon-sections.manic
// Animated reconstruction of a textbook sketch: horizontal and vertical
// sphere sections, then two perpendicular cuts producing 1/4 + 3/4.
// ============================================================================
title("Watermelon Sections — Slicing a Sphere");
canvas("9:16");
template("paper");
watermark(manicMark,(220,175),"Made With Manic");
creator(me,"@anish2good name=Manic_Geometry tagline=Textbooks_in_motion yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=cyan secondary=magenta footer=social safe=reels");
socials(me);
text(kicker,(540,164),"SECTIONS OF A SPHERE"); size(kicker,21); bold(kicker); color(kicker,cyan); hidden(kicker);
text(headline,(540,228),"How a watermelon becomes ½, ¼, and ¾"); size(headline,36); bold(headline); hidden(headline);
text(phase,(540,300),"THE TWO GREAT CIRCLES"); size(phase,23); bold(phase); color(phase,gold); hidden(phase);
text(narration,(540,1328),"A central plane cuts a sphere in a great circle."); size(narration,26); bold(narration); wrap(narration,850); hidden(narration);
text(principle,(540,1392),"Horizontal and vertical sections have the same radius."); size(principle,20); color(principle,dim); hidden(principle);
equation(formula,(540,1488),`x^2+y^2+z^2=R^2`,43); hidden(formula);
text(result,(540,1572),"ONE SPHERE · TWO PERPENDICULAR SECTION PLANES"); size(result,18); bold(result); color(result,gold); hidden(result);
// Stable isometric framing in the portrait Creator media region.
camera3((40.05,-39.35,23.41),(3.15,-2.45,-6.79),40);
// Shared textbook guides: the horizontal ring lies in z=0 and the vertical
// ring lies in y=0. Each chapter owns a duplicate so transitions stay simple.
// --------------------------------------------------------------------------
// Chapter 1 — horizontal cut, two hemispheres
// --------------------------------------------------------------------------
sphere3(hGuide,(0,0,0),3); color(hGuide,blue); hidden(hGuide); tag(hGuide,hScene);
finish3(hGuide,"material=glass shading=smooth mesh=0.18 depth=0.28 shadow=0.12");
curve3(hRing,"3*cos(t)","3*sin(t)","0",(0,6.2832)); color(hRing,cyan); thick(hRing,0.055); untraced(hRing); hidden(hRing); tag(hRing,hScene);
curve3(hVerticalRing,"3*cos(t)","0","3*sin(t)",(0,6.2832)); color(hVerticalRing,dim); thick(hVerticalRing,0.035); untraced(hVerticalRing); hidden(hVerticalRing); tag(hVerticalRing,hScene);
cube3(hPlane,(0,0,4.5),(7,7,0.08)); color(hPlane,cyan); hidden(hPlane); tag(hPlane,hScene);
finish3(hPlane,"material=glass shading=flat mesh=0.08 depth=0.14 shadow=0.04");
param3(hTopShell,
"3*sin(u)*cos(v)","3*sin(u)*sin(v)","3*cos(u)",
(0,1.5708),(0,6.2832),34);
color(hTopShell,lime); hidden(hTopShell); tag(hTopShell,hTop); tag(hTopShell,hScene);
finish3(hTopShell,"material=glass shading=smooth mesh=0.12 depth=0.24 shadow=0.10");
param3(hBottomShell,
"3*sin(u)*cos(v)","3*sin(u)*sin(v)","3*cos(u)",
(1.5708,3.1416),(0,6.2832),34);
color(hBottomShell,lime); hidden(hBottomShell); tag(hBottomShell,hBottom); tag(hBottomShell,hScene);
finish3(hBottomShell,"material=glass shading=smooth mesh=0.12 depth=0.24 shadow=0.10");
param3(hTopFace,"3*u*cos(v)","3*u*sin(v)","0",(0,1),(0,6.2832),30);
color(hTopFace,magenta); hidden(hTopFace); tag(hTopFace,hTop); tag(hTopFace,hScene);
finish3(hTopFace,"material=glass shading=flat mesh=0.30 depth=0.22 shadow=0.08");
param3(hBottomFace,"3*u*cos(v)","3*u*sin(v)","0",(0,1),(0,6.2832),30);
color(hBottomFace,magenta); hidden(hBottomFace); tag(hBottomFace,hBottom); tag(hBottomFace,hScene);
finish3(hBottomFace,"material=glass shading=flat mesh=0.30 depth=0.22 shadow=0.08");
// --------------------------------------------------------------------------
// Chapter 2 — vertical cut, front and back hemispheres
// --------------------------------------------------------------------------
sphere3(vGuide,(0,0,0),3); color(vGuide,blue); hidden(vGuide); tag(vGuide,vScene);
finish3(vGuide,"material=glass shading=smooth mesh=0.18 depth=0.28 shadow=0.12");
curve3(vRing,"3*cos(t)","0","3*sin(t)",(0,6.2832)); color(vRing,lime); thick(vRing,0.055); untraced(vRing); hidden(vRing); tag(vRing,vScene);
curve3(vHorizontalRing,"3*cos(t)","3*sin(t)","0",(0,6.2832)); color(vHorizontalRing,dim); thick(vHorizontalRing,0.035); untraced(vHorizontalRing); hidden(vHorizontalRing); tag(vHorizontalRing,vScene);
cube3(vPlane,(0,-4.5,0),(7,0.08,7)); color(vPlane,lime); hidden(vPlane); tag(vPlane,vScene);
finish3(vPlane,"material=glass shading=flat mesh=0.08 depth=0.14 shadow=0.04");
param3(vFrontShell,
"3*sin(u)*cos(v)","3*sin(u)*sin(v)","3*cos(u)",
(0,3.1416),(3.1416,6.2832),34);
color(vFrontShell,lime); hidden(vFrontShell); tag(vFrontShell,vFront); tag(vFrontShell,vScene);
finish3(vFrontShell,"material=glass shading=smooth mesh=0.12 depth=0.24 shadow=0.10");
param3(vBackShell,
"3*sin(u)*cos(v)","3*sin(u)*sin(v)","3*cos(u)",
(0,3.1416),(0,3.1416),34);
color(vBackShell,lime); hidden(vBackShell); tag(vBackShell,vBack); tag(vBackShell,vScene);
finish3(vBackShell,"material=glass shading=smooth mesh=0.12 depth=0.24 shadow=0.10");
param3(vFrontFace,"3*u*cos(v)","0","3*u*sin(v)",(0,1),(0,6.2832),30);
color(vFrontFace,cyan); hidden(vFrontFace); tag(vFrontFace,vFront); tag(vFrontFace,vScene);
finish3(vFrontFace,"material=glass shading=flat mesh=0.30 depth=0.22 shadow=0.08");
param3(vBackFace,"3*u*cos(v)","0","3*u*sin(v)",(0,1),(0,6.2832),30);
color(vBackFace,cyan); hidden(vBackFace); tag(vBackFace,vBack); tag(vBackFace,vScene);
finish3(vBackFace,"material=glass shading=flat mesh=0.30 depth=0.22 shadow=0.08");
// --------------------------------------------------------------------------
// Chapter 3 — perpendicular cuts, quarter removed and three quarters remain
// --------------------------------------------------------------------------
sphere3(qGuide,(0,0,0),3); color(qGuide,blue); hidden(qGuide); tag(qGuide,qScene);
finish3(qGuide,"material=glass shading=smooth mesh=0.18 depth=0.28 shadow=0.12");
curve3(qHRing,"3*cos(t)","3*sin(t)","0",(0,6.2832)); color(qHRing,cyan); thick(qHRing,0.045); untraced(qHRing); hidden(qHRing); tag(qHRing,qScene);
curve3(qVRing,"3*cos(t)","0","3*sin(t)",(0,6.2832)); color(qVRing,lime); thick(qVRing,0.045); untraced(qVRing); hidden(qVRing); tag(qVRing,qScene);
cube3(qHPlane,(0,0,4.5),(7,7,0.07)); color(qHPlane,cyan); hidden(qHPlane); tag(qHPlane,qScene);
cube3(qVPlane,(0,-4.5,0),(7,0.07,7)); color(qVPlane,lime); hidden(qVPlane); tag(qVPlane,qScene);
finish3(qHPlane,"material=glass shading=flat mesh=0.06 depth=0.12 shadow=0.03");
finish3(qVPlane,"material=glass shading=flat mesh=0.06 depth=0.12 shadow=0.03");
// Removed top-front quarter: one spherical patch plus two semicircular faces.
param3(qShell,
"3*sin(u)*cos(v)","3*sin(u)*sin(v)","3*cos(u)",
(0,1.5708),(3.1416,6.2832),32);
color(qShell,lime); hidden(qShell); tag(qShell,quarterPiece); tag(qShell,qScene);
finish3(qShell,"material=glass shading=smooth mesh=0.14 depth=0.26 shadow=0.12");
param3(qHFace,"3*u*cos(v)","3*u*sin(v)","0",(0,1),(3.1416,6.2832),28);
color(qHFace,gold); hidden(qHFace); tag(qHFace,quarterPiece); tag(qHFace,qScene);
finish3(qHFace,"material=glass shading=flat mesh=0.28 depth=0.22 shadow=0.08");
param3(qVFace,"3*u*cos(v)","0","3*u*sin(v)",(0,1),(0,3.1416),28);
color(qVFace,gold); hidden(qVFace); tag(qVFace,quarterPiece); tag(qVFace,qScene);
finish3(qVFace,"material=glass shading=flat mesh=0.28 depth=0.22 shadow=0.08");
// Remaining 3/4: top-back quarter plus the complete bottom hemisphere.
param3(rTopBack,
"3*sin(u)*cos(v)","3*sin(u)*sin(v)","3*cos(u)",
(0,1.5708),(0,3.1416),32);
color(rTopBack,lime); hidden(rTopBack); tag(rTopBack,remainder); tag(rTopBack,qScene);
finish3(rTopBack,"material=glass shading=smooth mesh=0.14 depth=0.26 shadow=0.12");
param3(rBottom,
"3*sin(u)*cos(v)","3*sin(u)*sin(v)","3*cos(u)",
(1.5708,3.1416),(0,6.2832),34);
color(rBottom,lime); hidden(rBottom); tag(rBottom,remainder); tag(rBottom,qScene);
finish3(rBottom,"material=glass shading=smooth mesh=0.14 depth=0.26 shadow=0.12");
param3(rHFace,"3*u*cos(v)","3*u*sin(v)","0",(0,1),(3.1416,6.2832),28);
color(rHFace,gold); hidden(rHFace); tag(rHFace,remainder); tag(rHFace,qScene);
finish3(rHFace,"material=glass shading=flat mesh=0.28 depth=0.22 shadow=0.08");
param3(rVFace,"3*u*cos(v)","0","3*u*sin(v)",(0,1),(0,3.1416),28);
color(rVFace,gold); hidden(rVFace); tag(rVFace,remainder); tag(rVFace,qScene);
finish3(rVFace,"material=glass shading=flat mesh=0.28 depth=0.22 shadow=0.08");
step("read the two central sections") {
seq {
par { show(kicker,0.35); show(headline,0.55); show(phase,0.40); show(narration,0.45); show(principle,0.40); show(formula,0.55); show(result,0.45); }
to(hGuide,opacity,0.28,0.80,smooth);
par { show(hRing,0.25); draw(hRing,1.20,smooth); show(hVerticalRing,0.25); draw(hVerticalRing,1.20,smooth); }
par { pulse(hRing,0.70); pulse(hVerticalRing,0.70); roll3(3,1.30,smooth); }
wait(0.65);
}
}
step("horizontal cut makes two halves") {
seq {
par {
say(phase,"HORIZONTAL SECTION",0.45,smooth);
say(narration,"Slide a horizontal plane through the centre: z=0.",0.55,smooth);
say(principle,"The intersection is a horizontal great circle.",0.50,smooth);
rewrite(formula,`z=0\quad\Longrightarrow\quad x^2+y^2=R^2`,1.00,smooth);
say(result,"CUT → TWO ½-WATERMELONS",0.45,smooth);
show(hPlane,0.30);
}
par { shift3(hPlane,(0,0,-4.5),2.00,smooth); pulse(hRing,0.75); }
par {
fade(hGuide,0.55); fade(hPlane,0.45); fade(hRing,0.45); fade(hVerticalRing,0.45);
show(hTopShell,0.40); show(hBottomShell,0.40);
to(hTopFace,opacity,0.74,0.50,smooth); to(hBottomFace,opacity,0.74,0.50,smooth);
}
par { shift3(hTop,(-3.8,0,0.7),1.85,smooth); shift3(hBottom,(1.5,0,-0.2),1.85,smooth); }
par { pulse(hTopFace,0.80); pulse(hBottomFace,0.80); }
wait(0.75);
}
}
step("vertical cut makes two halves") {
seq {
par {
fade(hScene,0.65);
say(phase,"VERTICAL SECTION",0.45,smooth);
say(narration,"Turn the cutting plane upright and pass it through y=0.",0.55,smooth);
say(principle,"Its section is the same-size circle, seen vertically.",0.50,smooth);
rewrite(formula,`y=0\quad\Longrightarrow\quad x^2+z^2=R^2`,1.00,smooth);
say(result,"ANOTHER CUT → TWO ½-WATERMELONS",0.45,smooth);
}
par { to(vGuide,opacity,0.26,0.70,smooth); show(vHorizontalRing,0.25); draw(vHorizontalRing,0.90,smooth); show(vRing,0.25); draw(vRing,1.10,smooth); }
par { show(vPlane,0.30); shift3(vPlane,(0,4.5,0),2.00,smooth); pulse(vRing,0.75); }
par {
fade(vGuide,0.55); fade(vPlane,0.45); fade(vRing,0.45); fade(vHorizontalRing,0.45);
show(vFrontShell,0.40); show(vBackShell,0.40);
to(vFrontFace,opacity,0.74,0.50,smooth); to(vBackFace,opacity,0.74,0.50,smooth);
}
par { shift3(vFront,(-3.8,0,0.4),1.85,smooth); shift3(vBack,(1.5,0,-0.1),1.85,smooth); }
wait(0.75);
}
}
step("perpendicular cuts remove one quarter") {
seq {
par {
fade(vScene,0.65);
say(phase,"TWO PERPENDICULAR CUTS",0.45,smooth);
say(narration,"Now combine the horizontal and vertical central planes.",0.55,smooth);
say(principle,"Each cut halves one independent direction.",0.50,smooth);
rewrite(formula,`\frac12\times\frac12=\textcolor{gold}{\frac14}`,1.00,smooth);
say(result,"¼ CUT OUT · ¾ REMAINS",0.45,smooth);
}
par { to(qGuide,opacity,0.24,0.65,smooth); show(qHRing,0.25); draw(qHRing,0.90,smooth); show(qVRing,0.25); draw(qVRing,0.90,smooth); }
seq {
par { show(qHPlane,0.25); shift3(qHPlane,(0,0,-4.5),1.35,smooth); }
par { show(qVPlane,0.25); shift3(qVPlane,(0,4.5,0),1.35,smooth); }
}
par {
fade(qGuide,0.50); fade(qHPlane,0.40); fade(qVPlane,0.40); fade(qHRing,0.40); fade(qVRing,0.40);
show(qShell,0.35); to(qHFace,opacity,0.78,0.45,smooth); to(qVFace,opacity,0.78,0.45,smooth);
show(rTopBack,0.35); show(rBottom,0.35); to(rHFace,opacity,0.70,0.45,smooth); to(rVFace,opacity,0.70,0.45,smooth);
}
par { shift3(quarterPiece,(-3.8,0,0.8),1.90,smooth); shift3(remainder,(1.4,0,-0.15),1.90,smooth); }
wait(0.70);
}
}
step("compare the pieces") {
seq {
par {
say(phase,"FRACTIONS ARE GEOMETRY",0.50,smooth);
say(narration,"The removed corner is one quarter; the connected remainder is three quarters.",0.55,smooth);
say(principle,"Horizontal section · vertical section · quarter volume",0.55,smooth);
rewrite(formula,`1-\frac14=\textcolor{gold}{\frac34}`,1.00,smooth);
show(result,0.25); say(result,"¼ PIECE + ¾ WATERMELON = 1 WHOLE",0.50,smooth);
}
par { pulse(quarterPiece,0.90); pulse(remainder,0.90); roll3(3,1.50,smooth); }
wait(2.00);
}
}
three_d
Cubes, spheres, arrows, a curve, a surface and solids together — the 3D basics on one stage.
title("3D coordinate space");
canvas(1280, 720);
template("terminal");
camera3((8, -10, 6), (0, 0, 1), 45);
grid3(floor, (0, 0, 0), 5, 1);
color(floor, dim);
axes3(world, (0, 0, 0), 4);
cube3(box, (0, 0, 1), (2, 2, 2));
color(box, magenta);
sphere3(ball, (-2, 1, 1), 0.7);
color(ball, lime);
arrow3(vector, (0, 0, 0), (2.5, 2, 3));
// a parametric helix (curve3), drawn on as a shaded tube
curve3(spiral, "cos(t)*2.6", "sin(t)*2.6", "t*0.32", (0, 12));
color(spiral, gold);
thick(spiral, 0.08);
// a height-field surface z = f(x,y) (surface3), filled + flat-shaded
surface3(wave, "0.6*sin(x)*cos(y)", (-3.5, 3.5), (-3.5, 3.5), 24);
color(wave, magenta);
opacity(wave, 0.5);
// filled, flat-shaded solids (prism3 / pyramid3)
prism3(hex, (-3.6, -2.4, 1.2), 6, 0.9, 2.2);
pyramid3(cone, (3.6, -2.4, 1.4), 22, 1.0, 2.6);
// a 2D label glued to the moving sphere — tracks it as the camera orbits
text(balltag, (0, 0), "ball");
color(balltag, gold);
pin3(balltag, ball);
par {
rotate3(box, (0, 0, 360), 4, linear);
orbit3(70, 28, 11, 4, smooth);
move3(ball, (2, -1, 2), 4, inout);
}
solids3
Filled, shaded solids: a prism, a cone, and a lathed vase.
title("3D solids");
canvas(1280, 720);
template("terminal");
camera3((9, -11, 7), (0, 0, 1), 45);
grid3(floor, (0, 0, 0), 6, 1);
color(floor, dim);
axes3(world, (0, 0, 0), 3);
// hexagonal prism
prism3(hex, (-4, 0, 1.2), 6, 1.1, 2.4);
color(hex, cyan);
// a cone (a pyramid with many sides)
pyramid3(cone, (0, 0, 1.4), 28, 1.2, 2.8);
color(cone, magenta);
// a lathed vase: radius profile r(t) swept around the vertical axis
revolve3(vase, (4, 0, 1.5), "0.7 + 0.45*sin(t*2.4)", (0, 3), 32);
color(vase, gold);
orbit3(60, 24, 17, 6, smooth);
param3
Parametric surfaces a height field can’t make — a torus, a sphere, and a Möbius strip.
// param3 — general parametric surfaces x(u,v), y(u,v), z(u,v). Unlike surface3
// (a height field z=f(x,y)) these can wrap and close, so a torus and a Möbius
// strip are just three formulas each.
//
// manic examples/param3.manic
// manic examples/param3.manic --record out --fps 60
title("parametric surfaces");
canvas("16:9");
template("terminal");
camera3((11, -13, 8.5), (0, 0, 1.4), 42);
grid3(floor, (0, 0, 0), 7, 1);
color(floor, dim);
// torus (left)
param3(torus,
"0 - 4.8 + (2 + 0.7*cos(v))*cos(u)",
"(2 + 0.7*cos(v))*sin(u)",
"1.5 + 0.7*sin(v)",
(0, 6.283), (0, 6.283), 40);
color(torus, cyan);
// parametric sphere (centre)
param3(ball,
"1.3*sin(v)*cos(u)",
"1.3*sin(v)*sin(u)",
"1.6 + 1.3*cos(v)",
(0, 6.283), (0, 3.1416), 28);
color(ball, magenta);
// Möbius strip (right)
param3(mobius,
"4.8 + (1.7 + v*cos(u/2))*cos(u)",
"(1.7 + v*cos(u/2))*sin(u)",
"1.6 + v*sin(u/2)",
(0, 6.283), (0 - 0.6, 0.6), 60);
color(mobius, gold);
show(floor, 0.3);
par { show(torus, 0.7); show(ball, 0.7); show(mobius, 0.7); }
orbit3(60, 24, 15, 5.0, smooth);
wait(0.4);
extrude3
Lifting flat shapes into solids, including a boolean cut-out (a plate with a hole) and an L-beam.
// Extrude & CSG — turn 2D shapes into 3D solids. `extrude3` sweeps any 2D
// fillable shape (or a boolean region) straight up; extruding a union/
// difference/intersect gives constructive-solid-geometry solids. The 2D
// source shapes are auto-hidden — they're just the cross-section recipe.
//
// manic examples/extrude3.manic
// manic examples/extrude3.manic --record out --fps 60
title("Extrude & CSG");
canvas(1280, 720);
template("terminal");
camera3((9, -11, 7), (0, 0, 0.8), 45);
grid3(floor, (0, 0, 0), 6, 1);
color(floor, dim);
axes3(world, (0, 0, 0), 3);
// CSG: a square plate MINUS a circular hole → an extruded plate-with-a-hole
rect(plate, (0, 0), 3, 3);
circle(hole, (0, 0), 0.9);
difference(cut, plate, hole);
extrude3(block, cut, 1.0, (-3.5, 0, 0.6));
color(block, cyan);
// arbitrary concave polygon: a union of two rects → an L-beam, extruded
rect(la, (0, 0), 2.4, 0.8);
rect(lb, (-0.8, 0.8), 0.8, 2.4);
union(lshape, la, lb);
extrude3(ell, lshape, 0.9, (3.5, 0, 0.55));
color(ell, magenta);
// a plain shape extrudes just as happily (a hexagon via a sector sweep)
sector(hexface, (0, 0), 1.2, 0, 360);
extrude3(disc, hexface, 0.5, (0, 3.5, 0.3));
color(disc, gold);
orbit3(60, 24, 17, 8, smooth);
morph3
Morphing across families — a cube into a sphere, a saddle into a bowl, a helix into a ring.
// 3D morphing (morph3) — set a 3D entity up to become another shape, then
// blend with `to(id, morph, 1, dur)`. Works across three families:
// • solids — a cube becomes a sphere (reparameterised spherically)
// • surfaces — a rippling saddle settles into a bowl (filled + shaded)
// • curves — a helix unwinds into a flat ring
//
// manic examples/morph3.manic
// manic examples/morph3.manic --record out --fps 60
title("3D morph");
canvas("16:9");
template("terminal");
camera3((11, -13, 8), (0, 0, 0.8), 42);
grid3(floor, (0, 0, 0), 7, 1);
color(floor, dim);
// solid: cube -> sphere
cube3(box, (-5, 0, 1.4), (2.4, 2.4, 2.4));
color(box, cyan);
sphere3(ball, (0, 0, 0), 1.5);
hidden(ball);
morph3(box, ball);
// surface: rippling saddle -> smooth bowl
surface3(saddle, "0.8*sin(x*1.3)*cos(y*1.3)", (-2.3, 2.3), (-2.3, 2.3), 24);
color(saddle, gold);
surface3(bowl, "0.16*(x*x + y*y) - 1", (-2.3, 2.3), (-2.3, 2.3), 24);
hidden(bowl);
morph3(saddle, bowl);
// curve: helix -> flat ring
curve3(helix, "5 + 1.5*cos(t)", "1.5*sin(t)", "0.2*t", (0, 18));
color(helix, magenta);
thick(helix, 0.06);
curve3(ring, "5 + 1.5*cos(t)", "1.5*sin(t)", "1.8", (0, 6.283));
hidden(ring);
morph3(helix, ring);
show(floor, 0.3);
par { show(box, 0.5); show(saddle, 0.5); show(helix, 0.5); }
wait(0.7);
par {
to(box, morph, 1, 3.0, smooth);
to(saddle, morph, 1, 3.0, smooth);
to(helix, morph, 1, 3.0, smooth);
orbit3(48, 25, 21, 3.0, smooth);
}
wait(0.8);
linear-algebra-3d — the essence, in 3D
The 3D companion to the linear-algebra lesson: one matrix
[[1,0,0],[0,3,1],[0,1,3]] (det 8; eigenvalues 1, 2, 4) seen two ways on an
orbiting stage — first as a transformation (the unit cube → a parallelepiped
whose volume is the determinant), then through its eigenvectors (the invariant
axes that only stretch). Start here for 3D.
// ============================================================================
// linear-algebra-3d.manic — Linear algebra in 3D (a lesson)
// ----------------------------------------------------------------------------
// The 3-D companion to linear-algebra.manic. One matrix, two lenses, one
// orbiting stage:
// 1. a 3x3 matrix TRANSFORMS space — the unit cube becomes a parallelepiped
// whose VOLUME is the determinant (linmap3)
// 2. its EIGENVECTORS are the invariant axes that only stretch (eigen3)
//
// Both use the same A = [[1,0,0],[0,3,1],[0,1,3]] (det = 8; eigenvalues 1,2,4),
// so the two views describe the very same matrix.
// ============================================================================
title("Linear algebra in 3D");
canvas("16:9");
camera3((12, 9.5, 10), (0.4, 1.6, 1.6), 40);
grid3(floor, (0, 0, 0), 4, 1); color(floor, dim); opacity(floor, 0.3);
// A = [[1,0,0],[0,3,1],[0,1,3]] — det 8, eigenvalues 1, 2, 4
linmap3(lm, (0, 0, 0), 1, 0, 0, 0, 3, 1, 0, 1, 3);
eigen3(ev, (0, 0, 0), 1, 0, 0, 0, 3, 1, 0, 1, 3);
// the eigen-axes wait until chapter 2
hidden(ev.axis0); hidden(ev.axis1); hidden(ev.axis2);
hidden(ev.l0); hidden(ev.l1); hidden(ev.l2);
text(cap, (cx, h - 46), ""); size(cap, 22); color(cap, dim); display(cap);
// ---- 1. the matrix as a transformation of space ----
section("A matrix in 3D");
say(cap, "a 3x3 matrix sends the unit cube to a parallelepiped — its columns", 0.5);
orbit3(50, 26, 13, 4, smooth);
say(cap, "the volume it encloses is the determinant: det = 8, so space grew 8x", 0.5);
orbit3(116, 30, 13, 4, smooth);
// clear the transformation view
fade(lm, 0.6); fade(lm.ref, 0.6);
fade(lm.i, 0.6); fade(lm.j, 0.6); fade(lm.k, 0.6);
fade(lm.li, 0.6); fade(lm.lj, 0.6); fade(lm.lk, 0.6); fade(lm.val, 0.6);
wait(0.3);
// ---- 2. the same matrix, through its eigenvectors ----
say(cap, "", 0.2);
section("Eigenvectors");
say(cap, "three special directions only STRETCH when A acts — the eigenvectors", 0.5);
show(ev.axis0, 0.5); show(ev.axis1, 0.5); show(ev.axis2, 0.5);
show(ev.l0, 0.4); show(ev.l1, 0.4); show(ev.l2, 0.4);
orbit3(58, 24, 7, 4, smooth);
say(cap, "their stretch factors are the eigenvalues: 1, 2 and 4", 0.5);
orbit3(122, 32, 6.5, 4.5, smooth);
wait(0.5);
linear-map3
Linear algebra in 3D: a 3×3 matrix deforms the unit cube into a parallelepiped,
with basis arrows i/j/k landing on the matrix’s columns and the enclosed volume
labelled as the determinant (linmap3). The 3D echo of linear-map.
// ============================================================================
// linear-map3.manic — a 3x3 matrix, in 3D (the 3-D echo of linear-map)
// ----------------------------------------------------------------------------
// `linmap3(id, (cx,cy,cz), a,b,c,d,e,f,g,h,i, [color])` applies the 3x3 matrix
// [[a,b,c],[d,e,f],[g,h,i]] to space: the unit cube (faint wireframe) becomes a
// parallelepiped, with basis arrows i/j/k landing on the matrix's COLUMNS. The
// volume it encloses IS the determinant (labelled) — it flips colour when the
// determinant is negative (orientation reversed) and collapses flat at det = 0.
//
// TO ADAPT: change the nine numbers. Try a shear (1,0.6,0, 0,1,0, 0,0,1) → det 1
// (volume unchanged), or swap two columns to make det negative (a reflection).
// ============================================================================
title("A 3x3 matrix, in 3D");
canvas("16:9");
camera3((7, 5.5, 6), (1, 1, 1), 40);
grid3(floor, (0, 0, 0), 4, 1); color(floor, dim); opacity(floor, 0.4);
// M = [[1.5,0.5,0],[0,1.5,0.5],[0.5,0,1.5]] — a skew + stretch, det = 3.5
linmap3(lm, (0, 0, 0), 1.5, 0.5, 0, 0, 1.5, 0.5, 0.5, 0, 1.5);
text(cap, (cx, h - 46), ""); size(cap, 22); color(cap, dim); display(cap);
say(cap, "a 3x3 matrix sends the unit cube to a parallelepiped", 0.5);
orbit3(58, 26, 10, 4, smooth);
say(cap, "i, j, k land on its columns — where the three axes are sent", 0.5);
orbit3(116, 32, 10, 4, smooth);
say(cap, "the volume enclosed IS the determinant: det = 3.5, so space grew 3.5x", 0.5);
orbit3(60, 20, 9, 4.5, smooth);
wait(0.5);
eigen3
The real eigenvectors of a 3×3 matrix, in 3D: the invariant lines through the
origin that only stretch (by λ) when the matrix acts (eigen3). The 3D echo of
eigen. A symmetric matrix gives three perpendicular real eigen-axes; a rotation
leaves one real axis and two complex eigenvalues.
// ============================================================================
// eigen3.manic — eigenvectors of a 3x3 matrix (the 3-D echo of eigen)
// ----------------------------------------------------------------------------
// `eigen3(id, (cx,cy,cz), a,b,c,d,e,f,g,h,i, [color])` draws the real
// EIGENVECTORS of the 3x3 [[a,b,c],[d,e,f],[g,h,i]] as invariant lines through
// the origin — the directions a vector on them only STRETCHES (by λ), never
// turns. A real 3x3 always has at least one real eigenvector; a rotation leaves
// two complex eigenvalues, which are noted.
//
// TO ADAPT: change the nine numbers. A symmetric matrix (as here) has three
// perpendicular real eigen-axes; a rotation like (0,-1,0, 1,0,0, 0,0,1) shows
// one real axis (the axis of rotation) + a "2 complex" note.
// ============================================================================
title("Eigenvectors of a 3x3 matrix");
canvas("16:9");
camera3((7, 5.5, 6), (0, 0, 0), 42);
grid3(floor, (0, 0, 0), 3, 1); color(floor, dim); opacity(floor, 0.35);
// symmetric M = [[1,0,0],[0,3,1],[0,1,3]] — three real eigenvalues: 1, 2, 4
eigen3(ev, (0, 0, 0), 1, 0, 0, 0, 3, 1, 0, 1, 3);
text(cap, (cx, h - 46), ""); size(cap, 22); color(cap, dim); display(cap);
say(cap, "when the matrix acts, most directions turn — but a few only stretch", 0.5);
orbit3(52, 24, 10, 4, smooth);
say(cap, "those invariant lines are the eigenvectors; the stretch factor is lambda", 0.5);
orbit3(122, 34, 10, 4, smooth);
say(cap, "this symmetric 3x3 has three real eigenvalues: 1, 2 and 4", 0.5);
orbit3(58, 22, 9, 4.5, smooth);
wait(0.5);
matrix3
A 3×3×3 block of cubes, with a shear matrix M and its inverse M⁻¹ applied and undone.
// A 3 × 3 × 3 block of 27 cubes, then a matrix operation: a 3×3 matrix is a
// linear map of space. We apply a shear M to every cell, then its inverse
// M^-1 — which sends the block back exactly, because M^-1 M = I.
//
// manic examples/matrix3.manic
// manic examples/matrix3.manic --record out --fps 60
title("3 × 3 × 3 · M and M^-1");
canvas("16:9");
template("terminal");
camera3((7.5, -9, 6), (2.4, 0, 2.1), 42);
grid3(floor, (0, 0, 0), 6, 1);
color(floor, dim);
let s = 1.3; // cell spacing (cubes are 1 wide → 0.3 gaps)
let zc = 2.1; // height of the block's centre
let a = 0.7; // shear amount: M sends x → x + a·z
text(cap, (cx, h - 42), "");
display(cap); color(cap, fg); size(cap, 21); hidden(cap);
// --- 27 cubes, three x-slices coloured cyan / magenta / lime ---
for j in 0..3 { for k in 0..3 {
cube3(xa{j}{k}, (0 - s, (j - 1)*s, zc + (k - 1)*s), (1, 1, 1));
color(xa{j}{k}, cyan); hidden(xa{j}{k});
} }
for j in 0..3 { for k in 0..3 {
cube3(xb{j}{k}, (0, (j - 1)*s, zc + (k - 1)*s), (1, 1, 1));
color(xb{j}{k}, magenta); hidden(xb{j}{k});
} }
for j in 0..3 { for k in 0..3 {
cube3(xc{j}{k}, (s, (j - 1)*s, zc + (k - 1)*s), (1, 1, 1));
color(xc{j}{k}, lime); hidden(xc{j}{k});
} }
// --- the matrices, as monospace text on the right ---
text(mLabel, (995, 232), "M =");
text(m0, (1035, 274), "[ 1 0 0.7 ]");
text(m1, (1035, 310), "[ 0 1 0 ]");
text(m2, (1035, 346), "[ 0 0 1 ]");
text(iLabel, (995, 446), "M^-1 =");
text(i0, (1035, 488), "[ 1 0 -0.7 ]");
text(i1, (1035, 524), "[ 0 1 0 ]");
text(i2, (1035, 560), "[ 0 0 1 ]");
display(mLabel); display(m0); display(m1); display(m2);
display(iLabel); display(i0); display(i1); display(i2);
color(mLabel, cyan); color(m0, cyan); color(m1, cyan); color(m2, cyan);
color(iLabel, gold); color(i0, gold); color(i1, gold); color(i2, gold);
size(mLabel, 24); size(m0, 22); size(m1, 22); size(m2, 22);
size(iLabel, 24); size(i0, 22); size(i1, 22); size(i2, 22);
hidden(mLabel); hidden(m0); hidden(m1); hidden(m2);
hidden(iLabel); hidden(i0); hidden(i1); hidden(i2);
// ------------------------------ script ------------------------------
show(floor, 0.3);
show(cap, 0.3);
say(cap, "A 3 by 3 by 3 block — 27 cells.", 0.7);
stagger(0.04) {
for j in 0..3 { for k in 0..3 { show(xa{j}{k}, 0.3); } }
for j in 0..3 { for k in 0..3 { show(xb{j}{k}, 0.3); } }
for j in 0..3 { for k in 0..3 { show(xc{j}{k}, 0.3); } }
}
orbit3(38, 22, 13, 1.6, smooth);
say(cap, "A 3×3 matrix M is a linear map of space. This M shears x by height z.", 1.0);
stagger(0.12) { show(mLabel, 0.3); show(m0, 0.3); show(m1, 0.3); show(m2, 0.3); }
// apply M: x' = x + a·(z - zc)
par {
for j in 0..3 { for k in 0..3 {
move3(xa{j}{k}, (0 - s + a*(k - 1)*s, (j - 1)*s, zc + (k - 1)*s), 1.8, smooth);
} }
for j in 0..3 { for k in 0..3 {
move3(xb{j}{k}, (a*(k - 1)*s, (j - 1)*s, zc + (k - 1)*s), 1.8, smooth);
} }
for j in 0..3 { for k in 0..3 {
move3(xc{j}{k}, (s + a*(k - 1)*s, (j - 1)*s, zc + (k - 1)*s), 1.8, smooth);
} }
}
say(cap, "Applying M slants the whole block — the higher a cell, the more it shifts.", 1.0);
stagger(0.12) { show(iLabel, 0.3); show(i0, 0.3); show(i1, 0.3); show(i2, 0.3); }
say(cap, "The inverse M^-1 just negates the shear.", 0.8);
// apply M^-1: sends every cell back to where it started
par {
for j in 0..3 { for k in 0..3 {
move3(xa{j}{k}, (0 - s, (j - 1)*s, zc + (k - 1)*s), 1.8, smooth);
} }
for j in 0..3 { for k in 0..3 {
move3(xb{j}{k}, (0, (j - 1)*s, zc + (k - 1)*s), 1.8, smooth);
} }
for j in 0..3 { for k in 0..3 {
move3(xc{j}{k}, (s, (j - 1)*s, zc + (k - 1)*s), 1.8, smooth);
} }
}
say(cap, "M^-1 sends every cell home: M^-1 M = I, the identity.", 1.0);
orbit3(70, 26, 13, 2.4, smooth);
wait(0.6);
double-integral3
Multivariable calculus: the volume under a surface as a limit of finer and finer columns — a double integral, made solid. The coarse blocks refine until they hug the surface.
title("The double integral — volume as a limit of finer columns");
canvas("16:9");
camera3((9, -11, 9), (0, 0, 0.7), 40);
axes3(world, (0, 0, 0), 3);
color(world, dim);
text(cap, (cx, h - 54), "coarse columns");
size(cap, 22); color(cap, cyan); bold(cap); display(cap); hidden(cap);
// the surface, kept faint so the columns beneath it read
surface3(hill, "1.6*exp(-(x*x + y*y)*0.28)", (-3, 3), (-3, 3), 30);
color(hill, cyan); opacity(hill, 0.22); untraced(hill);
// the volume under it, at three resolutions — each finer sum hugs the surface
volume3(v1, hill, 3);
color(v1, lime); hidden(v1);
volume3(v2, hill, 7);
color(v2, lime); hidden(v2);
volume3(v3, hill, 16);
color(v3, lime); hidden(v3);
// ---- timeline ----
show(world, 0.5);
draw(hill, 1.4);
wait(0.3);
// 3x3: a crude estimate
show(cap, 0.4);
show(v1, 0.7);
wait(1.2);
// 7x7: closer
fade(v1, 0.5);
say(cap, "finer: the sum gets closer");
show(v2, 0.7);
wait(1.2);
// 16x16: the columns fill the true volume under the surface
fade(v2, 0.5);
say(cap, "finer still -> the true volume (a double integral)");
show(v3, 0.8);
orbit3(70, 34, 16, 4.5);
multivariable3
Calculus on a surface: a smooth hill, its tangent plane and the gradient arrow at a chosen point, inspected with an orbiting camera.
title("Calculus on a surface — gradient, tangent plane, volume");
canvas("16:9");
camera3((8, -10, 7), (0, 0, 0.8), 42);
axes3(world, (0, 0, 0), 3);
color(world, dim);
// a smooth hill: z = 1.6 e^(-(x^2 + y^2)/3.6)
surface3(hill, "1.6*exp(-(x*x + y*y)*0.28)", (-3, 3), (-3, 3), 28);
color(hill, cyan); untraced(hill);
// the tangent plane at a point on the slope
tangentplane3(tp, hill, 1.1, -0.7);
color(tp, magenta); hidden(tp);
// the gradient there — an arrow pointing straight up the steepest slope
gradient3(grad, hill, 1.1, -0.7);
color(grad, gold); hidden(grad);
// ---- timeline ----
show(world, 0.6);
draw(hill, 1.8);
wait(0.4);
show(tp, 0.6);
show(grad, 0.6);
orbit3(70, 30, 15, 4.5);
volume3
The volume under a surface represented as solid 3D Riemann-sum columns, turning a double integral into visible geometry.
title("Volume under a surface — a double integral, made solid");
canvas("16:9");
camera3((9, -11, 8), (0, 0, 0.6), 40);
axes3(world, (0, 0, 0), 3);
color(world, dim);
// a smooth bump
surface3(hill, "1.6*exp(-(x*x + y*y)*0.28)", (-3, 3), (-3, 3), 28);
color(hill, cyan); untraced(hill);
// the volume beneath it, as a grid of columns (a 3D Riemann sum)
volume3(vol, hill, 11);
color(vol, lime); hidden(vol);
// ---- timeline ----
show(world, 0.6);
draw(hill, 1.6);
wait(0.4);
show(vol, 1.0);
orbit3(70, 32, 16, 4.5);
heightmap3
The Grid Kit → 3D bridge: heightmap3(land, grid, "z(x,y,h)") lifts a 2-D grid’s per-cell
state into a surface3-style terrain mesh. A seeded Wave Function Collapse settles a map,
then its walls rise into an island terrain a camera orbits — the grid kit stays entirely
3D-unaware.
// heightmap3 — a Grid Kit → 3D bridge. A 2D grid's per-cell state is lifted into a
// surface3-style terrain mesh. The grid kit stays entirely 3D-unaware; heightmap3
// lives on the 3D side and just reads the grid's cells. Here a seeded Wave Function
// Collapse settles a map, then its walls rise into an island terrain a camera orbits.
title("Heightmap3 — A Grid Becomes Terrain");
canvas("16:9");
template("neon");
// A grid-kit map: `collapse` pre-simulates a seeded WFC settle (clustered walls).
grid(mz, (cx, cy), 18, 14, 30);
collapse(mz, "islands", 5);
hidden(mz.cells); hidden(mz.lines); // we only want the 3D terrain, not the flat grid
// Lift it: h = 1 for a filled cell, 0 otherwise. The formula raises the walls and
// blends in organic `fbm` noise for a natural roll — grid value AND noise compose.
camera3((0, -12, 7), (0, 0, 0.7), 32);
heightmap3(land, mz, "h*1.5 + fbm(x*1.1, y*1.1)*0.85 + 0.1", 7);
color(land, cyan);
// A slow turntable so the terrain reads in 3D.
orbit3(70, 34, 13, 4.5, smooth);
orbit3(200, 30, 13, 4.5, smooth);
orbit3(330, 36, 13, 4.5, smooth);
heightmap3-world
The creative payoff: a grid-kit WFC map settles in 2-D, then the very same grid lifts into
a 3-D world as the camera tilts down — one grid, two dimensions. h (the cell value) is a
third formula variable added to the expression engine for exactly this.
// From Flat Map to 3D World — the creative payoff of the Grid→3D bridge. A grid-kit
// Wave Function Collapse settles a map in 2D; then the very same grid is lifted by
// heightmap3 into a 3D terrain that rises as the camera tilts down. One grid, two
// dimensions — the grid kit never learns about 3D.
title("From Flat Map to 3D World");
canvas("16:9");
template("neon");
let u = (w+h-abs(w-h))/1440;
watermark(mk, (w*0.86, h*0.06), "Made With Manic");
// The 2D map (grid kit): a seeded WFC settle.
grid(world, (cx, cy), 20, 16, 30);
collapse(world, "maze", 11);
// The 3D terrain from the SAME grid — near top-down at first, so it reads flat like
// the map, then the tilt reveals its height. Hidden until the lift.
camera3((0, -1.6, 20), (0, 0, 0), 24);
heightmap3(land, world, "h*1.9 + fbm(x*1.2, y*1.2)*0.9 + 0.1", 8);
color(land, cyan);
hidden(land);
text(cap, (cx, h*0.9), "a flat 2D map…");
size(cap, 30*u); color(cap, dim); wrap(cap, w*0.7);
// 1 — the map settles in 2D (Wave Function Collapse).
step("settle") {
par {
say(cap, "a flat 2D map, generated cell by cell…");
run(world, 16, 3.4);
}
}
wait(0.4);
// 2 — the same grid lifts into 3D: fade the flat map, reveal the terrain (still
// near top-down, so it still reads flat — the map has just gained a third axis).
step("lift") {
par {
say(cap, "…is secretly a 3D world");
fade(world.cells, 0.6);
fade(world.lines, 0.6);
show(land, 0.7);
}
}
// 3 — pull back and tilt so the WHOLE world is in frame at once.
step("reveal") {
orbit3(25, 38, 27, 3.8, smooth);
}
wait(0.4);
// 4 — a slow rotation around the full terrain.
step("rotate") {
seq {
say(cap, "one grid — 2D pathfinding AND 3D terrain, bridged by manic");
orbit3(170, 34, 27, 5.2, smooth);
}
}
// 5 — then fly in close and low over the peaks. That's manic.
step("zoom") {
orbit3(235, 15, 10, 4.6, smooth);
}
wait(1.2);
noise-terrain
Procedural generation from a single formula: noise(x,y) and fbm(x,y) (fractal Brownian
motion) are now formula functions beside sin/cos, so surface3(land, "fbm(x*0.9,y*0.9)*2.4")
sculpts an organic fractal landscape the camera tours — no new kit, just two functions the
shared expression evaluator now understands.
// Fractal Terrain — procedural generation from a single formula. `noise(x,y)` and
// `fbm(x,y)` (fractal Brownian motion — five octaves of smooth value noise) are now
// formula functions, right beside sin/cos, so any `surface3`/`heightmap3`/`plot`
// formula can be organic. Here fbm alone sculpts a whole landscape the camera tours.
title("Fractal Terrain — Noise in a Formula");
canvas("16:9");
template("neon");
camera3((0, -13, 6.5), (0, 0, 0.3), 28);
surface3(land, "fbm(x*0.9, y*0.9)*2.4", (-4, 4), (-4, 4), 72);
color(land, cyan);
// A slow turntable so the fractal relief reads in 3D.
orbit3(60, 30, 13, 4.5, smooth);
orbit3(190, 26, 13, 4.5, smooth);
orbit3(320, 32, 13, 4.5, smooth);