Generative & recursive
Each block is the whole file — copy it into x.manic and run manic x.manic (live) or --record out (video).
gun-shot
A pure-imagination SCENE — no physics kit, just storytelling: a gun fires, the camera
flies along with the bullet (cam/zoom), a block drops in out of nowhere, and BOOM —
flash/shake/pulse + a for-loop spark burst. manic as a movie language.
// ============================================================================
// gun-shot.manic — a scene, not a lesson. No physics kit, just imagination.
// ----------------------------------------------------------------------------
// A gun fires · the camera races along with the bullet · a block drops in out
// of nowhere · BOOM. Built entirely from base manic — shapes, `move`, `cam`/
// `zoom` to fly the camera, `flash`/`shake`/`pulse`, and a `for`-loop spark
// burst. This is manic as a storytelling language: dream a scene, write it.
// ============================================================================
title("Gun Shot");
canvas("16:9");
// ---- the world (wide — the camera pans across it) ----
line(ground, (-300, 560), (2400, 560)); color(ground, dim); stroke(ground, 4);
// the gun: barrel + body + grip
rect(barrel, (250, 470), 96, 22); color(barrel, dim); filled(barrel);
rect(body, (206, 478), 52, 42); color(body, dim); filled(body);
polygon(grip, (186, 500), (220, 500), (212, 554), (180, 550), dim);
// the bullet at the muzzle, and a muzzle flash — both waiting
circle(bullet, (302, 470), 12); color(bullet, gold); glow(bullet, 2.2); hidden(bullet);
circle(mflash, (312, 470), 30); color(mflash, gold); glow(mflash, 3.5); hidden(mflash);
// the block — waiting above, off-screen, to drop in ahead
rect(block, (1750, 250), 130, 130); color(block, cyan); filled(block); glow(block, 1.4); hidden(block);
text(boom, (1750, 320), "BOOM!"); size(boom, 96); color(boom, magenta); bold(boom); glow(boom, 2.5); display(boom); hidden(boom);
// a ring of impact sparks around the block (revealed at the hit)
for i in 0..14 {
let ang = i * tau / 14.0;
line(spark{i}, (1700, 470), (1700 + 160*cos(ang), 470 + 160*sin(ang)));
color(spark{i}, gold); stroke(spark{i}, 5); glow(spark{i}, 2); untraced(spark{i}); tag(spark{i}, sparks);
}
// a caption pinned to the screen (rides along through the camera move)
text(cap, (cx, h - 56), ""); color(cap, fg); size(cap, 26); bold(cap); display(cap); sticky(cap);
// ================= THE SCENE =================
cam((440, 380), 0.4, smooth); // frame the gun
say(cap, "steady…", 0.4);
wait(0.6);
// FIRE!
say(cap, "FIRE!", 0.2);
par { show(mflash, 0.06); pulse(mflash); show(bullet, 0.08); }
fade(mflash, 0.3);
// the bullet races off — the camera flies with it — and mid-flight, out of
// nowhere, a block slams down into its path
par {
move(bullet, (1690, 470), 2.6, smooth);
cam((1560, 380), 2.6, smooth);
zoom(1.15, 2.6, smooth);
seq {
wait(1.5);
say(cap, "…wait — what's THAT?!", 0.3);
show(block, 0.1);
move(block, (1750, 470), 0.4, bounce);
}
}
// BOOM — impact
say(cap, "BOOM!", 0.15);
par {
flash(block, gold);
shake(block, 0.5);
zoom(1.5, 0.15);
show(boom, 0.12); pulse(boom);
draw(sparks, 0.35);
}
wait(0.5);
// settle — pull back
par {
fade(sparks, 0.5);
fade(boom, 0.6);
fade(bullet, 0.4);
zoom(1.0, 0.9, smooth);
}
say(cap, "…scene.", 0.4);
wait(0.8);
fractal_tree
One recursive def, drawn to depth 12.
// Fractal Tree — a recursive `def` macro draws a branching tree. Each branch
// splits into two shorter branches at a fixed angle; `if depth > 0` is the base
// case that stops the recursion. Branches are keyed by a binary-heap index
// (k -> 2k, 2k+1) so every segment gets a unique id, hued and thinned by depth.
//
// Showcases the Phase-2 language layer: `def`, recursion, `if`, comparisons.
//
// manic examples/fractal_tree.manic
// manic examples/fractal_tree.manic --record out --fps 60
title("Fractal Tree");
canvas(1280, 720);
text(head, (640, 92), "one recursive rule, drawn to depth 9");
display(head); color(head, cyan); size(head, 26); hidden(head);
// draw a branch, then recurse into two children (unless we've bottomed out)
def branch(k, x, y, ang, len, depth) {
// stop at the base depth OR once a branch is too short to see — so even a
// large `depth` self-limits (the tree is bounded by branch length)
if depth > 0 && len > 2 {
let x2 = x + len * cos(ang);
let y2 = y - len * sin(ang); // screen y grows downward
line(seg{k}, (x, y), (x2, y2));
stroke(seg{k}, 1 + depth * 0.8);
hue(seg{k}, 120 + depth * 15); // trunk bluish -> tips green
untraced(seg{k}); tag(seg{k}, tree);
branch(2*k, x2, y2, ang + 0.42, len * 0.72, depth - 1);
branch(2*k + 1, x2, y2, ang - 0.42, len * 0.72, depth - 1);
}
}
// grow from the bottom centre, pointing up (angle pi/2)
branch(1, 640, 700, 1.5708, 150, 20);
// --- script ---
show(head, 0.5);
draw(tree, 1.8);
wait(1.6);
particles-flow
Contained ambient motion and live curved connections in four generic words: particles,
wander, link, and flow. The ids supply the domain meaning.
// Generic contained motion: the ids give the dots their meaning.
// The same four words work for bubbles, dust, stars, data, or molecules.
title("Three bodies, one relation");
canvas("9:16");
watermark(manicMark, (w*0.955-100, h*0.045+24), "Made With Manic");
// No template call: mono is the polished black-and-white default.
circle(A, (540, 390), 105);
circle(B, (260, 760), 105);
circle(C, (820, 760), 105);
stroke(A, 5); stroke(B, 5); stroke(C, 5);
particles(insideA, A, 24, 5, 7);
particles(insideB, B, 24, 5, 17);
particles(insideC, C, 24, 5, 27);
equation(labelA, (540, 390), `A`, 64);
equation(labelB, (260, 760), `B`, 64);
equation(labelC, (820, 760), `C`, 64);
link(ab, A, B, -48);
link(bc, B, C, -56);
link(ac, A, C, 48);
stroke(ab, 5); stroke(bc, 5); stroke(ac, 5);
untraced(ab); untraced(bc); untraced(ac);
equation(relAB, (310, 445), `A\sim B`, 34);
equation(relBC, (540, 690), `B\sim C`, 34);
equation(relAC, (770, 445), `A\sim C`, 34);
color(relAB, dim); color(relBC, dim); color(relAC, dim);
hidden(A); hidden(B); hidden(C);
hidden(labelA); hidden(labelB); hidden(labelC);
hidden(insideA); hidden(insideB); hidden(insideC);
hidden(relAB); hidden(relBC); hidden(relAC);
par {
wander(insideA, 9);
wander(insideB, 9);
wander(insideC, 9);
seq {
par { show(A, 0.35); show(labelA, 0.35); show(insideA, 0.45); }
wait(0.25);
par { show(B, 0.35); show(labelB, 0.35); show(insideB, 0.45); }
show(relAB, 0.25);
par { draw(ab, 0.75); recolor(relAB, fg, 0.75); }
flow(ab, 0.9);
par { show(C, 0.35); show(labelC, 0.35); show(insideC, 0.45); }
show(relBC, 0.25);
par { draw(bc, 0.75); recolor(relBC, fg, 0.75); }
flow(bc, 0.9);
show(relAC, 0.25);
par { draw(ac, 0.75); recolor(relAC, fg, 0.75); }
par { flow(ab, 1.1); flow(bc, 1.1); flow(ac, 1.1); }
wait(0.55);
}
}
hue_wave
An animated hue wave across a grid.
// Hue Wave — a ring of dots, each with its own starting hue, all advancing
// their hue at the same rate so the rainbow *rotates* around the ring. Shows
// off `hue` as an animatable track: `to(id, hue, degrees)` cycles colour over
// time (unlike `recolor`, it travels around the colour wheel, not through grey).
//
// manic examples/hue_wave.manic
// manic examples/hue_wave.manic --record out --fps 60
title("Hue Wave");
canvas(1280, 720);
text(head, (640, 110), "an animated hue track — colour that cycles");
display(head); color(head, cyan); size(head, 26); hidden(head);
let n = 36; let cx = 640; let cy = 400; let r = 210;
// a ring of dots, rainbow-coloured by angle
for i in 0..n {
let a = tau * i / n;
dot(d{i}, (cx + r*cos(a), cy + r*sin(a)), 18);
hue(d{i}, 360 * i / n);
glow(d{i}, 1.4);
tag(d{i}, ring);
}
// --- script ---
show(head, 0.5);
// spin the whole rainbow: every dot advances its hue by 720 deg (two full
// cycles) over 6s, in parallel — the pattern rotates around the ring
par {
for i in 0..n {
to(d{i}, hue, 360*i/n + 720, 6.0, linear);
}
}
hill_run
A little scene animated with the language layer.
// Uphill / Downhill — a rate x time = distance word problem.
// "Up a hill at 4 mph, back down the same path at 6 mph, round trip = 1 hour.
// Total distance?" Answer: one-way d = 2.4 mi, round trip = 4.8 mi.
//
// The distance is SOLVED in-language: d = 1 / (1/4 + 1/6) = 2.4, total = 2d.
// The runner climbs slowly, descends faster (3s vs 2s ~ the real 0.6h : 0.4h),
// then the equation is derived and the answer counts up on a live readout.
//
// manic examples/hill_run.manic
// manic examples/hill_run.manic --record out --fps 60
title("Uphill / Downhill");
canvas("16:9");
// --- the numbers, computed the same way you'd reason it out ---
let up = 4; // mph, uphill
let down = 6; // mph, downhill
let d = 1 / (1/up + 1/down); // one-way distance = 2.4 mi (from d/4 + d/6 = 1)
let total = 2 * d; // round trip = 4.8 mi
text(head, (cx, 84), "up at 4 mph, down at 6 mph -- round trip takes 1 hour");
display(head); color(head, cyan); size(head, 24); hidden(head);
text(cap, (cx, 668), ""); color(cap, dim); size(cap, 23);
// --- the hill (a single path, run up then down) ---
line(ground, (150, 560), (700, 560)); color(ground, dim); stroke(ground, 2); untraced(ground);
line(path, (200, 560), (620, 210)); color(path, cyan); stroke(path, 4); untraced(path);
text(flag, (628, 196), "top"); color(flag, dim); size(flag, 18); hidden(flag);
dot(runner, (200, 560), 16); color(runner, lime); glow(runner, 1.7); hidden(runner);
text(uplbl, (300, 470), "4 mph"); color(uplbl, cyan); size(uplbl, 24); hidden(uplbl);
text(downlbl, (520, 320), "6 mph"); color(downlbl, magenta); size(downlbl, 24); hidden(downlbl);
// --- the derivation, on the right ---
text(e1, (960, 230), "time = distance / rate"); color(e1, dim); size(e1, 22); hidden(e1);
text(e2, (960, 300), "d/4 + d/6 = 1"); display(e2); color(e2, fg); size(e2, 30); hidden(e2);
text(e3, (960, 360), "5d/12 = 1 -> d = 2.4"); display(e3); color(e3, cyan); size(e3, 26); hidden(e3);
counter(ans, (960, 450), 0, 1, "round trip = 2d = ", " mi"); display(ans); color(ans, lime); size(ans, 30); hidden(ans);
// --- script ---
show(head, 0.5);
say(cap, "an athlete runs up a hill, then back down the same path");
par { draw(ground, 0.5); draw(path, 0.7); }
par { show(flag, 0.3); show(runner, 0.3); }
wait(0.3);
section("Up the hill");
say(cap, "uphill at 4 mph -- the slow leg");
show(uplbl, 0.3);
move(runner, (620, 210), 3.0, linear);
section("Back down");
say(cap, "downhill at 6 mph -- faster, so less time");
show(downlbl, 0.3);
move(runner, (200, 560), 2.0, linear);
wait(0.3);
section("Set up the equation");
say(cap, "let d = the one-way distance; time = distance / rate");
show(e1, 0.4);
show(e2, 0.4);
say(cap, "combine the fractions: 5d/12 = 1, so d = 2.4 miles");
show(e3, 0.5);
flash(e3, lime);
section("Total distance");
say(cap, "the round trip is 2d");
show(ans, 0.3);
to(ans, value, total, 1.4);
pulse(ans);
wait(1.6);
walk
An articulated stick figure walking down a road — legs swing, arms counter-swing, the body
bobs — built purely from the language layer (let + for + trig), no character rig.
title("A Generic Figure Walking Down the Road");
canvas("16:9");
let groundY = cy + 160;
let startX = cx - 420;
let stepDist = 15;
let swingAmp = 26;
let bobAmp = 10;
// ================= road =================
rect(road, (0, groundY), w, h - groundY);
color(road, dim);
filled(road);
untraced(road);
line(roadLine, (0, groundY + 40), (w, groundY + 40));
color(roadLine, panel);
stroke(roadLine, 2);
untraced(roadLine);
for i in 0..12 {
rect(dash{i}, (i*120 - 40, groundY + 36), 50, 8);
color(dash{i}, fg);
filled(dash{i});
untraced(dash{i});
}
// ================= stick figure as points + reflowing segments =================
point(neck, (startX, groundY - 118));
point(hip, (startX, groundY - 10));
point(handL, (startX - 30, groundY - 40));
point(handR, (startX + 30, groundY - 40));
point(footL, (startX - 30, groundY + 100));
point(footR, (startX + 30, groundY + 100));
hidden(neck);
hidden(hip);
hidden(handL);
hidden(handR);
hidden(footL);
hidden(footR);
circle(head, (startX, groundY - 140), 22);
color(head, fg);
outlined(head);
stroke(head, 3);
untraced(head);
segment(spine, neck, hip);
segment(armL, neck, handL);
segment(armR, neck, handR);
segment(legL, hip, footL);
segment(legR, hip, footR);
color(spine, fg);
color(armL, cyan);
color(armR, cyan);
color(legL, gold);
color(legR, gold);
stroke(spine, 4);
stroke(armL, 4);
stroke(armR, 4);
stroke(legL, 4);
stroke(legR, 4);
untraced(spine);
untraced(armL);
untraced(armR);
untraced(legL);
untraced(legR);
// ================= text =================
text(head_label, (cx, 55), "A Generic Figure Walking Down the Road");
color(head_label, cyan);
hidden(head_label);
text(caption, (cx, h - 30), "");
color(caption, dim);
hidden(caption);
// ================= script =================
show(head_label, 0.6);
wait(0.3);
par {
draw(road, 0.5);
draw(roadLine, 0.5);
stagger(0.03) {
for i in 0..12 {
draw(dash{i}, 0.1);
}
}
}
par {
show(neck, 0.01); show(hip, 0.01);
show(handL, 0.01); show(handR, 0.01);
show(footL, 0.01); show(footR, 0.01);
draw(head, 0.4);
draw(spine, 0.3);
draw(armL, 0.3);
draw(armR, 0.3);
draw(legL, 0.3);
draw(legR, 0.3);
}
wait(0.3);
show(caption, 0.4);
say(caption, "Camera pulls back to see the whole road");
par {
cam((cx, cy), 1.0, smooth);
zoom(0.85, 1.0, smooth);
}
wait(0.3);
// --- walk cycle: phase steps by 90 deg so sin actually alternates ---
for i in 0..28 {
let baseX = startX + i*stepDist;
let phase = i*90;
let swing = swingAmp*sin(phase*pi/180);
let legLift = bobAmp*abs(sin(phase*pi/180));
par {
move(neck, (baseX, groundY - 118 - legLift*0.4), 0.15, smooth);
move(hip, (baseX, groundY - 10), 0.15, smooth);
move(handL, (baseX - swing, groundY - 40), 0.15, smooth);
move(handR, (baseX + swing, groundY - 40), 0.15, smooth);
move(footL, (baseX + swing, groundY + 100 - legLift), 0.15, smooth);
move(footR, (baseX - swing, groundY + 100 - legLift), 0.15, smooth);
move(head, (baseX, groundY - 140 - legLift*0.4), 0.15, smooth);
}
}
wait(0.2);
say(caption, "Camera zooms in as the figure gets close");
par {
cam((startX + 420, groundY - 80), 1.4, smooth);
zoom(2.2, 1.4, smooth);
}
wait(0.4);
say(caption, "A close-up look, then pulling back out");
par {
cam((cx, cy), 1.2, smooth);
zoom(1, 1.2, smooth);
}
wait(0.4);
show(caption, 0.3);
say(caption, "A generic stick figure walking -- no specific person depicted");
two_person_walk
Two figures walk toward each other, MEET in the middle, shake hands, then continue past — a little choreographed scene from loops and arithmetic alone (the language layer as animation).
title("Two Figures Meet, Shake Hands, and Continue Walking");
canvas("16:9");
let groundY = cy + 160;
let startX1 = cx - 420;
let startX2 = cx + 420;
let stepDist = 15;
let swingAmp = 26;
let bobAmp = 10;
let meetX = cx;
let endX1 = cx + 420;
let endX2 = cx - 420;
// ================= road =================
rect(road, (0, groundY), w, h - groundY);
color(road, dim);
filled(road);
untraced(road);
line(roadLine, (0, groundY + 40), (w, groundY + 40));
color(roadLine, panel);
stroke(roadLine, 2);
untraced(roadLine);
for i in 0..14 {
rect(dash{i}, (i*120 - 40, groundY + 36), 50, 8);
color(dash{i}, fg);
filled(dash{i});
untraced(dash{i});
}
// ================= figure 1 (walks left -> right) =================
point(neck1, (startX1, groundY - 118));
point(hip1, (startX1, groundY - 10));
point(handL1, (startX1 - 30, groundY - 40));
point(handR1, (startX1 + 30, groundY - 40));
point(footL1, (startX1 - 30, groundY + 100));
point(footR1, (startX1 + 30, groundY + 100));
hidden(neck1); hidden(hip1);
hidden(handL1); hidden(handR1);
hidden(footL1); hidden(footR1);
circle(head1, (startX1, groundY - 140), 22);
color(head1, fg);
outlined(head1);
stroke(head1, 3);
untraced(head1);
segment(spine1, neck1, hip1);
segment(armL1, neck1, handL1);
segment(armR1, neck1, handR1);
segment(legL1, hip1, footL1);
segment(legR1, hip1, footR1);
color(spine1, fg);
color(armL1, cyan);
color(armR1, cyan);
color(legL1, gold);
color(legR1, gold);
stroke(spine1, 4); stroke(armL1, 4); stroke(armR1, 4);
stroke(legL1, 4); stroke(legR1, 4);
untraced(spine1); untraced(armL1); untraced(armR1);
untraced(legL1); untraced(legR1);
// ================= figure 2 (walks right -> left, mirrored) =================
point(neck2, (startX2, groundY - 118));
point(hip2, (startX2, groundY - 10));
point(handL2, (startX2 - 30, groundY - 40));
point(handR2, (startX2 + 30, groundY - 40));
point(footL2, (startX2 - 30, groundY + 100));
point(footR2, (startX2 + 30, groundY + 100));
hidden(neck2); hidden(hip2);
hidden(handL2); hidden(handR2);
hidden(footL2); hidden(footR2);
circle(head2, (startX2, groundY - 140), 22);
color(head2, fg);
outlined(head2);
stroke(head2, 3);
untraced(head2);
segment(spine2, neck2, hip2);
segment(armL2, neck2, handL2);
segment(armR2, neck2, handR2);
segment(legL2, hip2, footL2);
segment(legR2, hip2, footR2);
color(spine2, fg);
color(armL2, magenta);
color(armR2, magenta);
color(legL2, lime);
color(legR2, lime);
stroke(spine2, 4); stroke(armL2, 4); stroke(armR2, 4);
stroke(legL2, 4); stroke(legR2, 4);
untraced(spine2); untraced(armL2); untraced(armR2);
untraced(legL2); untraced(legR2);
// ================= text =================
text(head_label, (cx, 55), "Two Figures Meet, Shake Hands, and Continue Walking");
color(head_label, cyan);
hidden(head_label);
text(caption, (cx, h - 30), "");
color(caption, dim);
hidden(caption);
// ================= script =================
show(head_label, 0.6);
wait(0.3);
par {
draw(road, 0.5);
draw(roadLine, 0.5);
stagger(0.03) {
for i in 0..14 {
draw(dash{i}, 0.1);
}
}
}
par {
show(neck1, 0.01); show(hip1, 0.01);
show(handL1, 0.01); show(handR1, 0.01);
show(footL1, 0.01); show(footR1, 0.01);
draw(head1, 0.4);
draw(spine1, 0.3);
draw(armL1, 0.3);
draw(armR1, 0.3);
draw(legL1, 0.3);
draw(legR1, 0.3);
show(neck2, 0.01); show(hip2, 0.01);
show(handL2, 0.01); show(handR2, 0.01);
show(footL2, 0.01); show(footR2, 0.01);
draw(head2, 0.4);
draw(spine2, 0.3);
draw(armL2, 0.3);
draw(armR2, 0.3);
draw(legL2, 0.3);
draw(legR2, 0.3);
}
wait(0.3);
show(caption, 0.4);
say(caption, "Camera pulls back to see the whole road");
par {
cam((cx, cy), 1.0, smooth);
zoom(0.85, 1.0, smooth);
}
wait(0.3);
// --- walk cycle: both figures walk toward each other, meeting at meetX ---
for i in 0..24 {
let baseX1 = startX1 + i*stepDist;
let baseX2 = startX2 - i*stepDist;
let phase = i*90;
let swing = swingAmp*sin(phase*pi/180);
let legLift = bobAmp*abs(sin(phase*pi/180));
par {
move(neck1, (baseX1, groundY - 118 - legLift*0.4), 0.15, smooth);
move(hip1, (baseX1, groundY - 10), 0.15, smooth);
move(handL1, (baseX1 - swing, groundY - 40), 0.15, smooth);
move(handR1, (baseX1 + swing, groundY - 40), 0.15, smooth);
move(footL1, (baseX1 + swing, groundY + 100 - legLift), 0.15, smooth);
move(footR1, (baseX1 - swing, groundY + 100 - legLift), 0.15, smooth);
move(head1, (baseX1, groundY - 140 - legLift*0.4), 0.15, smooth);
move(neck2, (baseX2, groundY - 118 - legLift*0.4), 0.15, smooth);
move(hip2, (baseX2, groundY - 10), 0.15, smooth);
move(handL2, (baseX2 - swing, groundY - 40), 0.15, smooth);
move(handR2, (baseX2 + swing, groundY - 40), 0.15, smooth);
move(footL2, (baseX2 + swing, groundY + 100 - legLift), 0.15, smooth);
move(footR2, (baseX2 - swing, groundY + 100 - legLift), 0.15, smooth);
move(head2, (baseX2, groundY - 140 - legLift*0.4), 0.15, smooth);
}
}
wait(0.2);
say(caption, "They arrive face to face");
par {
cam((meetX, groundY - 80), 1.2, smooth);
zoom(1.8, 1.2, smooth);
}
// settle into a standing pose facing each other
par {
move(neck1, (meetX - 40, groundY - 118), 0.3, smooth);
move(hip1, (meetX - 40, groundY - 10), 0.3, smooth);
move(footL1, (meetX - 60, groundY + 100), 0.3, smooth);
move(footR1, (meetX - 20, groundY + 100), 0.3, smooth);
move(head1, (meetX - 40, groundY - 140), 0.3, smooth);
move(handL1, (meetX - 70, groundY - 40), 0.3, smooth);
move(neck2, (meetX + 40, groundY - 118), 0.3, smooth);
move(hip2, (meetX + 40, groundY - 10), 0.3, smooth);
move(footL2, (meetX + 60, groundY + 100), 0.3, smooth);
move(footR2, (meetX + 20, groundY + 100), 0.3, smooth);
move(head2, (meetX + 40, groundY - 140), 0.3, smooth);
move(handR2, (meetX + 70, groundY - 40), 0.3, smooth);
}
wait(0.3);
say(caption, "Reaching out to shake hands");
par {
move(handR1, (meetX - 5, groundY - 55), 0.4, smooth);
move(handL2, (meetX + 5, groundY - 55), 0.4, smooth);
}
wait(0.2);
par {
move(handR1, (meetX, groundY - 55), 0.25, smooth);
move(handL2, (meetX, groundY - 55), 0.25, smooth);
}
wait(0.2);
say(caption, "Shaking hands");
for i in 0..4 {
par {
move(handR1, (meetX, groundY - 65), 0.12, smooth);
move(handL2, (meetX, groundY - 65), 0.12, smooth);
}
par {
move(handR1, (meetX, groundY - 48), 0.12, smooth);
move(handL2, (meetX, groundY - 48), 0.12, smooth);
}
}
par {
move(handR1, (meetX, groundY - 55), 0.15, smooth);
move(handL2, (meetX, groundY - 55), 0.15, smooth);
}
wait(0.3);
flash(handR1, gold);
flash(handL2, gold);
wait(0.3);
say(caption, "Letting go and continuing on their separate ways");
par {
cam((cx, cy), 1.2, smooth);
zoom(1, 1.2, smooth);
}
// release hands back to normal swing position before resuming walk
par {
move(handR1, (meetX - 40 + 30, groundY - 40), 0.25, smooth);
move(handL2, (meetX + 40 - 30, groundY - 40), 0.25, smooth);
}
wait(0.2);
// --- resume walk cycle: figure1 continues toward endX1, figure2 toward endX2 ---
for i in 0..24 {
let baseX1 = (meetX - 40) + i*stepDist;
let baseX2 = (meetX + 40) - i*stepDist;
let phase = i*90;
let swing = swingAmp*sin(phase*pi/180);
let legLift = bobAmp*abs(sin(phase*pi/180));
par {
move(neck1, (baseX1, groundY - 118 - legLift*0.4), 0.15, smooth);
move(hip1, (baseX1, groundY - 10), 0.15, smooth);
move(handL1, (baseX1 - swing, groundY - 40), 0.15, smooth);
move(handR1, (baseX1 + swing, groundY - 40), 0.15, smooth);
move(footL1, (baseX1 + swing, groundY + 100 - legLift), 0.15, smooth);
move(footR1, (baseX1 - swing, groundY + 100 - legLift), 0.15, smooth);
move(head1, (baseX1, groundY - 140 - legLift*0.4), 0.15, smooth);
move(neck2, (baseX2, groundY - 118 - legLift*0.4), 0.15, smooth);
move(hip2, (baseX2, groundY - 10), 0.15, smooth);
move(handL2, (baseX2 - swing, groundY - 40), 0.15, smooth);
move(handR2, (baseX2 + swing, groundY - 40), 0.15, smooth);
move(footL2, (baseX2 + swing, groundY + 100 - legLift), 0.15, smooth);
move(footR2, (baseX2 - swing, groundY + 100 - legLift), 0.15, smooth);
move(head2, (baseX2, groundY - 140 - legLift*0.4), 0.15, smooth);
}
}
wait(0.3);
say(caption, "Two generic stick figures -- no specific persons depicted");
par {
cam((cx, cy), 1.0, smooth);
zoom(0.85, 1.0, smooth);
}
wait(0.4);
equal_cuts
A circle halved again and again (pizza cuts).
// Equal Cuts — a circle sliced into equal pieces, repeatedly doubled:
// 2 → 4 → 8 equal wedges. Each "cut" is a diameter traced across the circle
// at an equal angle. (manic has no sector primitive yet, so cuts are lines.)
//
// manic examples/equal_cuts.manic
// manic examples/equal_cuts.manic --record out --fps 60
title("Equal Cuts");
canvas(1280, 720);
// the circle to divide, centred at (640, 400) with radius 240
circle(pie, (640, 400), 240); stroke(pie, 3);
// four diameters through the centre at 0, 45, 90, 135 degrees.
// revealed in stages, they cut the circle into 2, then 4, then 8 equal pieces.
line(c0, (400, 400), (880, 400)); color(c0, magenta); stroke(c0, 3); untraced(c0); // 0
line(c1, (640, 160), (640, 640)); color(c1, magenta); stroke(c1, 3); untraced(c1); // 90
line(c2, (470, 230), (810, 570)); color(c2, lime); stroke(c2, 3); untraced(c2); // 135
line(c3, (810, 230), (470, 570)); color(c3, lime); stroke(c3, 3); untraced(c3); // 45
text(cap, (640, 690), ""); color(cap, dim); size(cap, 22);
text(count, (1040, 170), ""); color(count, cyan); size(count, 34); bold(count);
// --- cut in half ---
say(cap, "cut the circle in half");
draw(c0, 0.6);
say(count, "2 pieces");
wait(0.5);
// --- cut again: four equal pieces ---
say(cap, "cut again at a right angle — four equal pieces");
draw(c1, 0.6);
say(count, "4 pieces");
wait(0.5);
// --- and again: eight equal pieces ---
say(cap, "and again on both diagonals — eight equal pieces");
par {
draw(c2, 0.6);
draw(c3, 0.6);
}
say(count, "8 pieces");
pulse(pie);
wait(1.2);
archimedes_pi
Bounding pi with inscribed / circumscribed polygons.
// Approximating pi — Archimedes' method (c. 250 BC): inscribe a regular polygon
// in a circle and its perimeter closes in on the circumference. For an n-gon in
// a circle of radius R the perimeter is 2R * n*sin(pi/n), so pi ~ n*sin(pi/n),
// which -> pi as n grows. We sweep n = 6, 24, 96 (Archimedes' own 96-gon) and
// zoom in to see the last polygon nearly kiss the circle.
//
// Uses: a `for` loop per polygon, computed estimates, a live counter, and the
// camera (cam + zoom).
//
// manic examples/archimedes_pi.manic
// manic examples/archimedes_pi.manic --record out --fps 60
title("Approximating pi");
canvas("16:9");
let ox = 440; let oy = 400; let R = 240; // circle centre + radius
// the estimates, computed in-language
let e6 = 6 * sin(pi/6); // 3.000
let e24 = 24 * sin(pi/24); // 3.133
let e96 = 96 * sin(pi/96); // 3.141
text(head, (640, 78), "Archimedes: straight lines closing in on a circle");
display(head); color(head, cyan); size(head, 25); hidden(head);
text(cap, (640, 675), ""); color(cap, dim); size(cap, 22);
// the true circle (the target)
circle(circ, (ox, oy), R); outlined(circ); outline(circ, dim); stroke(circ, 2); untraced(circ);
// live pi readout
counter(est, (990, 330), 0, 3, "pi ~ ", ""); display(est); color(est, lime); size(est, 40); hidden(est);
text(truth, (990, 395), "true pi = 3.14159..."); color(truth, dim); size(truth, 20); hidden(truth);
// --- hexagon: n = 6 (magenta) ---
let n = 6;
for i in 0..n {
let a0 = tau*i/n; let a1 = tau*(i+1)/n;
line(h{i}, (ox + R*cos(a0), oy + R*sin(a0)), (ox + R*cos(a1), oy + R*sin(a1)));
color(h{i}, magenta); stroke(h{i}, 3); untraced(h{i}); tag(h{i}, p6);
}
// --- 24-gon (cyan) ---
let n = 24;
for i in 0..n {
let a0 = tau*i/n; let a1 = tau*(i+1)/n;
line(g{i}, (ox + R*cos(a0), oy + R*sin(a0)), (ox + R*cos(a1), oy + R*sin(a1)));
color(g{i}, cyan); stroke(g{i}, 3); untraced(g{i}); tag(g{i}, p24);
}
// --- 96-gon (lime), Archimedes' own ---
let n = 96;
for i in 0..n {
let a0 = tau*i/n; let a1 = tau*(i+1)/n;
line(k{i}, (ox + R*cos(a0), oy + R*sin(a0)), (ox + R*cos(a1), oy + R*sin(a1)));
color(k{i}, lime); stroke(k{i}, 2); untraced(k{i}); tag(k{i}, p96);
}
// --- script ---
show(head, 0.5);
say(cap, "how close can straight lines get to a curve?");
draw(circ, 1.0);
par { show(est, 0.3); show(truth, 0.3); }
wait(0.4);
section("6 sides");
say(cap, "start with a hexagon inside the circle");
draw(p6, 0.8);
to(est, value, e6, 1.0);
wait(0.7);
fade(p6, 0.4);
section("24 sides");
say(cap, "more sides hug the circle more tightly");
draw(p24, 1.0);
to(est, value, e24, 1.0);
wait(0.7);
fade(p24, 0.4);
section("96 sides");
say(cap, "Archimedes went to 96 sides -- around 250 BC");
draw(p96, 1.2);
to(est, value, e96, 1.0);
pulse(est);
wait(0.7);
section("Almost a circle");
say(cap, "zoom in: the polygon edge and the arc nearly touch");
par { cam((ox, oy - R), 1.5, smooth); zoom(5, 1.5, smooth); }
wait(1.4);
par { cam((cx, cy), 1.0, smooth); zoom(1, 1.0, smooth); }
wait(0.8);
pieday
A Pi Day card: a rainbow petal-flower built from a loop of circles, radial rays,
the digits of π, and the definition circumference / diameter = pi.
title("Pi Day");
canvas("16:9");
let r = h*0.23;
let centerY = cy + 25;
let n = 64;
let petalsN = 12;
text(head, (cx, 70), "Happy Pi Day");
text(bigPi, (cx, centerY - 8), "pi");
text(digits, (cx, h - 92), "3.1415926535897932384626433832795028841971...");
text(formula, (cx, h - 50), "circumference / diameter = pi");
size(head, 40);
size(bigPi, 112);
size(digits, 24);
size(formula, 26);
bold(head);
bold(bigPi);
color(head, magenta);
color(bigPi, gold);
color(digits, cyan);
color(formula, lime);
hidden(head);
hidden(bigPi);
hidden(digits);
hidden(formula);
circle(mainCircle, (cx, centerY), r);
line(diameter, (cx - r, centerY), (cx + r, centerY));
text(diamLab, (cx, centerY + 34), "diameter");
text(circLab, (cx, centerY - r - 30), "circumference");
stroke(mainCircle, 5);
stroke(diameter, 3);
color(mainCircle, cyan);
color(diameter, lime);
color(diamLab, lime);
color(circLab, cyan);
size(diamLab, 22);
size(circLab, 22);
hidden(diamLab);
hidden(circLab);
untraced(mainCircle);
untraced(diameter);
for i in 0..petalsN {
circle(petal{i}, (cx + 0.54*r*cos(tau*i/petalsN), centerY + 0.54*r*sin(tau*i/petalsN)), 0.46r);
stroke(petal{i}, 2);
hue(petal{i}, 360i/petalsN);
opacity(petal{i}, 0.34);
untraced(petal{i});
tag(petal{i}, petals);
}
for i in 0..n {
dot(spark{i}, (cx + 1.23*r*cos(tau*i/n), centerY + 1.23*r*sin(tau*i/n)), 4);
hue(spark{i}, 360*i/n);
hidden(spark{i});
tag(spark{i}, sparks);
}
for i in 0..24 {
line(ray{i}, (cx + 1.02*r*cos(tau*i/24), centerY + 1.02*r*sin(tau*i/24)), (cx + 1.18*r*cos(tau*i/24), centerY + 1.18*r*sin(tau*i/24)));
stroke(ray{i}, 3);
hue(ray{i}, 360*i/24);
untraced(ray{i});
tag(ray{i}, rays);
}
dot(centerDot, (cx, centerY), 6);
color(centerDot, gold);
hidden(centerDot);
show(head, 0.7);
par {
draw(petals, 1.4);
draw(mainCircle, 1.2);
}
par {
draw(diameter, 0.8);
show(centerDot, 0.4);
show(diamLab, 0.5);
show(circLab, 0.5);
}
par {
show(bigPi, 0.9);
draw(rays, 0.9);
}
stagger(0.018) {
for i in 0..n {
show(spark{i}, 0.25);
}
}
par {
show(digits, 0.7);
show(formula, 0.7);
}
pulse(bigPi, 0.8);
pulse(mainCircle, 0.8);
par {
spin(petals, 18, 3.0, smooth);
spin(sparks, -35, 3.0, smooth);
}
wait(1.2);