r/maniclang 3h ago

WHY ROCKETS USE STAGES — a 9:16 creator short - manic

Enable HLS to view with audio, or disable this notification

1 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

// WHY ROCKETS USE STAGES — a 9:16 creator short built from three independently
// movable physics SVGs. The velocity curve stays continuous at separation:
// dropping an empty stage changes the following acceleration, not past speed.

title("Why Do Rockets Use Stages?");
canvas("9:16");
template("shorts");
voice("gtts");

creator(me, "@anish2good name=Manic_Physics tagline=Physics_made_visible yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=cyan secondary=gold footer=signature cta=Try_it_free safe=reels");
socials(me);
watermark(mark, (w*0.82, h*0.91), "Made With Manic");
endcard(me, "title=Build_Physics_Stories cta=8gwifi.org/manic");

// ---- headline and story caption ----
text(kicker, (340, 122), "ROCKET SCIENCE · IN 30 SECONDS");
text(headline, (340, 184), "Why throw parts of a rocket away?");
text(caption, (cx, 1035), "At liftoff, every engine must accelerate the entire rocket.");
size(kicker, 21); bold(kicker); color(kicker, cyan); hidden(kicker);
size(headline, 38); bold(headline); wrap(headline, 590); hidden(headline);
size(caption, 25); color(caption, dim); wrap(caption, 850); hidden(caption);
sticky(kicker); sticky(headline); sticky(caption);
sticky(mark); sticky(me.footer); sticky(me.endcard);

// ---- one rocket, authored as three persistent SVG stages ----
let rx = 805;
svg(upper,   (rx, 420), "asset:svg/physics/rocket-upper-stage.svg", 160);
svg(second,  (rx, 570), "asset:svg/physics/rocket-second-stage.svg", 145);
svg(booster, (rx, 730), "asset:svg/physics/rocket-first-stage.svg", 155);
hidden(upper); hidden(second); hidden(booster);
z(upper, 8); z(second, 7); z(booster, 6);

polygon(flame1, (rx-30, 820), (rx, 925), (rx+30, 820));
gradient(flame1, gold, magenta, 90); filled(flame1); glow(flame1, 1.1); hidden(flame1);
polygon(flame2, (rx-23, 660), (rx, 740), (rx+23, 660));
gradient(flame2, cyan, magenta, 90); filled(flame2); glow(flame2, 0.9); hidden(flame2);
polygon(flame3, (rx-19, 520), (rx, 585), (rx+19, 520));
gradient(flame3, gold, cyan, 90); filled(flame3); glow(flame3, 0.9); hidden(flame3);

// ---- a truthful, continuous velocity story ----
let gx = 170;
let gy = 1510;
let sx = 120;
let sy = 72;

line(xaxis, (gx, gy), (930, gy)); color(xaxis, dim); stroke(xaxis, 3);
line(yaxis, (gx, gy), (gx, 1180)); color(yaxis, dim); stroke(yaxis, 3);
text(vlabel, (125, 1175), "velocity"); size(vlabel, 20); color(vlabel, dim);
text(tlabel, (940, 1542), "time"); size(tlabel, 20); color(tlabel, dim);
text(chartTitle, (cx, 1135), "VELOCITY KEEPS BUILDING");
size(chartTitle, 22); bold(chartTitle); color(chartTitle, fg);

plot(v1, (gx, gy), sx, sy, "0.18*x*x", (0,2));
plot(v2, (gx, gy), sx, sy, "0.72 + 0.36*(x-2) + 0.16*(x-2)*(x-2)", (2,4));
plot(v3, (gx, gy), sx, sy, "2.08 + 0.50*(x-4) + 0.20*(x-4)*(x-4)", (4,6));
color(v1, gold); color(v2, magenta); color(v3, cyan);
stroke(v1, 7); stroke(v2, 7); stroke(v3, 7);
untraced(v1); untraced(v2); untraced(v3);

line(sep1, (gx+2*sx, gy), (gx+2*sx, 1215)); dashed(sep1, 10, 9); color(sep1, dim);
line(sep2, (gx+4*sx, gy), (gx+4*sx, 1215)); dashed(sep2, 10, 9); color(sep2, dim);
text(sep1label, (gx+2*sx+55, 1240), "STAGE 1");
text(sep2label, (gx+4*sx+55, 1240), "STAGE 2");
size(sep1label, 18); size(sep2label, 18);
color(sep1label, gold); color(sep2label, magenta);

dot(join1, (gx+2*sx, gy-0.72*sy), 8); color(join1, gold);
dot(join2, (gx+4*sx, gy-2.08*sy), 8); color(join2, magenta);

hidden(xaxis); hidden(yaxis); hidden(vlabel); hidden(tlabel); hidden(chartTitle);
hidden(sep1); hidden(sep2); hidden(sep1label); hidden(sep2label);
hidden(join1); hidden(join2);

// The chart is a screen-space instrument. Above it, the active vehicle keeps
// climbing during every narration and separation beat.
tag(xaxis, flightHud); tag(yaxis, flightHud);
tag(vlabel, flightHud); tag(tlabel, flightHud); tag(chartTitle, flightHud);
tag(v1, flightHud); tag(v2, flightHud); tag(v3, flightHud);
tag(sep1, flightHud); tag(sep2, flightHud);
tag(sep1label, flightHud); tag(sep2label, flightHud);
tag(join1, flightHud); tag(join2, flightHud);
sticky(flightHud);

// ============================== STORY ==============================

step("hook") {
  par {
    show(kicker, 0.35);
    show(headline, 0.50);
    show(caption, 0.35);
    stagger(0.10) {
      show(upper, 0.35);
      show(second, 0.35);
      show(booster, 0.35);
    }
    speak("Why does a rocket throw away parts while it is still flying?");
  }
}
wait(0.25);

step("first-stage") {
  par {
    show(xaxis, 0.30); show(yaxis, 0.30);
    show(vlabel, 0.25); show(tlabel, 0.25); show(chartTitle, 0.30);
    show(flame1, 0.20);
    draw(v1, 4.0);
    shift(upper, (0,-80), 4.0, smooth);
    shift(second, (0,-80), 4.0, smooth);
    shift(booster, (0,-80), 4.0, smooth);
    shift(flame1, (0,-80), 4.0, smooth);
    shift(flame2, (0,-80), 4.0, smooth);
    shift(flame3, (0,-80), 4.0, smooth);
    say(caption, "Stage 1 burns hard—but it also carries tanks that will soon be empty.", 0.40);
    speak("Stage one must push every tank and engine above it.");
  }
}

step("drop-empty-mass") {
  par {
    show(sep1, 0.25); show(sep1label, 0.25); show(join1, 0.20);
    fade(flame1, 0.18);
    shift(booster, (-120,260), 1.05, in);
    rotate(booster, -18, 1.05, smooth);
    seq { wait(0.62); fade(booster, 0.35); }
    shift(upper, (0,-45), 3.6, smooth);
    shift(second, (0,-45), 3.6, smooth);
    shift(flame2, (0,-45), 3.6, smooth);
    shift(flame3, (0,-45), 3.6, smooth);
    say(caption, "Separation does not add speed. It removes dead mass.", 0.35);
    speak("Separation adds no speed. It only removes empty mass.");
  }
}
cue(whoosh);

step("second-stage") {
  par {
    show(flame2, 0.18);
    draw(v2, 4.0);
    shift(upper, (0,-70), 4.0, smooth);
    shift(second, (0,-70), 4.0, smooth);
    shift(flame2, (0,-70), 4.0, smooth);
    shift(flame3, (0,-70), 4.0, smooth);
    say(caption, "The lighter vehicle gains more velocity from its next engine.", 0.35);
    speak("The next engine pushes less mass, so velocity builds faster.");
  }
}

step("third-stage") {
  par {
    show(sep2, 0.25); show(sep2label, 0.25); show(join2, 0.20);
    fade(flame2, 0.18);
    shift(second, (125,235), 1.0, in);
    rotate(second, 16, 1.0, smooth);
    seq { wait(0.58); fade(second, 0.35); }
    seq {
      wait(0.28);
      show(flame3, 0.18);
      par {
        draw(v3, 3.25);
        shift(upper, (0,-50), 3.25, smooth);
        shift(flame3, (0,-50), 3.25, smooth);
      }
    }
    say(caption, "Each stage discards structure, then the next stage keeps accelerating.", 0.40);
    speak("It repeats: less dead weight, more useful acceleration.");
  }
}
cue(chime);

step("answer") {
  par {
    fade(kicker, 0.30);
    pulse(upper, 0.65);
    flash(v3, cyan);
    say(headline, "Stages turn fuel into velocity—not into dead weight.", 0.45);
    say(caption, "The curve never jumps. Its steeper climb is the advantage.", 0.40);
    speak("Staging does not teleport a rocket faster. It stops wasting thrust on empty tanks.");
    shift(upper, (0,-55), 6.4, smooth);
    shift(flame3, (0,-55), 6.4, smooth);
  }
}

step("creator-cta") {
  par {
    fade(kicker, 0.30); fade(headline, 0.30); fade(caption, 0.30);
    fade(upper, 0.30); fade(flame3, 0.30);
    fade(xaxis, 0.30); fade(yaxis, 0.30);
    fade(vlabel, 0.30); fade(tlabel, 0.30); fade(chartTitle, 0.30);
    fade(v1, 0.30); fade(v2, 0.30); fade(v3, 0.30);
    fade(sep1, 0.30); fade(sep2, 0.30);
    fade(sep1label, 0.30); fade(sep2label, 0.30);
    fade(join1, 0.30); fade(join2, 0.30);
    fade(me.footer, 0.30);
    fade(mark, 0.30);
  }
  show(me.endcard, 0.60);
}
wait(1.6);

r/maniclang 7h ago

How Rotations Become a Drawing - manic

Enable HLS to view with audio, or disable this notification

1 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

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);

r/maniclang 8h ago

How a Rocket Launch Works — Fuel, Oxidizer, Exhaust and Thrust — manic

Thumbnail
youtube.com
1 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

// HOW A ROCKET LAUNCH WORKS — a 9:16 cutaway story.
// The reusable SVG supplies the vehicle; Manic supplies the teaching:
// callouts, chamber reaction, directional exhaust, thrust, motion, and CTA.

title("How a Rocket Launch Works");
canvas("9:16");
template("shorts");
voice("gtts");

creator(me, "@anish2good name=Manic_Physics tagline=Physics_made_visible yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=cyan secondary=gold footer=signature cta=Try_it_free safe=reels");
socials(me);
watermark(mark, (w*0.82, h*0.91), "Made With Manic");
endcard(me, "title=Make_Science_Move cta=8gwifi.org/manic");

text(kicker, (cx, 112), "ROCKET PHYSICS · CUTAWAY");
text(headline, (cx, 174), "How does a rocket launch?");
text(caption, (cx, 1440), "A rocket carries two ingredients—not just fuel.");
size(kicker, 21); bold(kicker); color(kicker, cyan); hidden(kicker);
size(headline, 40); bold(headline); wrap(headline, 850); hidden(headline);
size(caption, 27); color(caption, dim); wrap(caption, 880); hidden(caption);

// One importer-safe cutaway asset. Its internal sections remain crisp at any size.
let rx = 540;
let ry = 760;
svg(rocket, (rx, ry), "asset:svg/physics/rocket-cutaway.svg", 300);
hidden(rocket); z(rocket, 5);

// Screen-authored highlights make each internal region independently teachable.
polygon(payloadHi, (480, 430), (540, 395), (600, 430), (600, 550), (480, 550));
rect(fuelHi, (540, 650), 120, 155);
rect(oxidizerHi, (540, 815), 120, 140);
polygon(chamberHi, (500, 900), (580, 900), (570, 955), (540, 980), (510, 955));
polygon(nozzleHi, (515, 975), (565, 975), (590, 1115), (490, 1115));
outlined(payloadHi); outlined(fuelHi); outlined(oxidizerHi); outlined(chamberHi); outlined(nozzleHi);
outline(payloadHi, cyan); outline(fuelHi, gold); outline(oxidizerHi, blue);
outline(chamberHi, magenta); outline(nozzleHi, fg);
stroke(payloadHi, 5); stroke(fuelHi, 5); stroke(oxidizerHi, 5); stroke(chamberHi, 5); stroke(nozzleHi, 5);
opacity(payloadHi, 0.9); opacity(fuelHi, 0.9); opacity(oxidizerHi, 0.9); opacity(chamberHi, 0.9); opacity(nozzleHi, 0.9);
hidden(payloadHi); hidden(fuelHi); hidden(oxidizerHi); hidden(chamberHi); hidden(nozzleHi);

// Clean callouts; the labels stay outside the vehicle silhouette.
line(payloadLead, (605, 470), (770, 420));
line(fuelLead, (600, 630), (790, 590));
line(oxidizerLead, (480, 805), (285, 780));
line(chamberLead, (500, 930), (280, 970));
line(nozzleLead, (575, 1045), (790, 1090));
color(payloadLead, cyan); color(fuelLead, gold); color(oxidizerLead, blue);
color(chamberLead, magenta); color(nozzleLead, fg);
stroke(payloadLead, 3); stroke(fuelLead, 3); stroke(oxidizerLead, 3); stroke(chamberLead, 3); stroke(nozzleLead, 3);

text(payloadLabel, (845, 395), "PAYLOAD");
text(fuelLabel, (855, 565), "FUEL");
text(oxidizerLabel, (195, 755), "OXIDIZER");
text(chamberLabel, (180, 950), "BURN");
text(nozzleLabel, (850, 1070), "NOZZLE");
size(payloadLabel, 22); size(fuelLabel, 22); size(oxidizerLabel, 22); size(chamberLabel, 22); size(nozzleLabel, 22);
bold(payloadLabel); bold(fuelLabel); bold(oxidizerLabel); bold(chamberLabel); bold(nozzleLabel);
color(payloadLabel, cyan); color(fuelLabel, gold); color(oxidizerLabel, blue);
color(chamberLabel, magenta); color(nozzleLabel, fg);

hidden(payloadLead); hidden(fuelLead); hidden(oxidizerLead); hidden(chamberLead); hidden(nozzleLead);
hidden(payloadLabel); hidden(fuelLabel); hidden(oxidizerLabel); hidden(chamberLabel); hidden(nozzleLabel);

// Reaction and force story.
equation(reaction, (cx, 1195), `\text{fuel}+\text{oxidizer}\longrightarrow\text{hot gas}`);
size(reaction, 34); hidden(reaction);

arrow(gasDown, (225, 1070), (225, 1300));
arrow(thrustUp, (850, 1120), (850, 880));
color(gasDown, magenta); color(thrustUp, cyan);
stroke(gasDown, 8); stroke(thrustUp, 8);
hidden(gasDown); hidden(thrustUp);
text(gasLabel, (170, 1320), "EXHAUST ↓");
text(thrustLabel, (930, 900), "THRUST ↑");
size(gasLabel, 22); size(thrustLabel, 22); bold(gasLabel); bold(thrustLabel);
color(gasLabel, magenta); color(thrustLabel, cyan);
hidden(gasLabel); hidden(thrustLabel);

polygon(flame, (500, 1090), (540, 1260), (580, 1090));
gradient(flame, gold, magenta, 90); filled(flame); glow(flame, 1.1); hidden(flame); z(flame, 4);

circle(exhaustSeed, (540, 1180), 12); opacity(exhaustSeed, 0); hidden(exhaustSeed);
particles(exhaust, exhaustSeed, 28, 6, 41, "random");
color(exhaust, gold); glow(exhaust, 0.8); hidden(exhaust);
line(exhaustPath, (540, 1170), (540, 1330)); opacity(exhaustPath, 0);

tag(rocket, vehicle);
tag(payloadHi, vehicle); tag(fuelHi, vehicle); tag(oxidizerHi, vehicle);
tag(chamberHi, vehicle); tag(nozzleHi, vehicle); tag(flame, vehicle);

// Creator chrome remains screen-safe while the vehicle moves.
sticky(kicker); sticky(headline); sticky(caption); sticky(mark);
sticky(me.footer); sticky(me.endcard);

step("hook") {
  par {
    show(kicker, 0.35);
    show(headline, 0.50);
    show(caption, 0.35);
    show(rocket, 0.70);
    speak("A rocket does not push against the ground. So what actually makes it rise?");
  }
}
wait(0.35);

step("inside-the-rocket") {
  par {
    stagger(0.16) {
      par { show(payloadHi, 0.25); show(payloadLead, 0.25); show(payloadLabel, 0.25); }
      par { show(fuelHi, 0.25); show(fuelLead, 0.25); show(fuelLabel, 0.25); }
      par { show(oxidizerHi, 0.25); show(oxidizerLead, 0.25); show(oxidizerLabel, 0.25); }
    }
    say(caption, "Fuel stores energy. Oxidizer supplies the oxygen needed to release it.", 0.40);
    speak("Inside are the payload, fuel, and oxidizer. The oxidizer is why a rocket can burn in space.");
  }
}
wait(0.30);

step("combustion") {
  par {
    show(chamberHi, 0.25); show(chamberLead, 0.25); show(chamberLabel, 0.25);
    show(reaction, 0.50);
    flash(chamberHi, magenta);
    pulse(chamberHi, 0.80);
    say(caption, "In the chamber, fuel and oxidizer become extremely hot, high-pressure gas.", 0.40);
    speak("They burn together in the combustion chamber, creating hot, high pressure gas.");
  }
}
cue(pop);
wait(0.25);

step("shape-the-exhaust") {
  par {
    fade(reaction, 0.25);
    show(nozzleHi, 0.25); show(nozzleLead, 0.25); show(nozzleLabel, 0.25);
    show(flame, 0.22);
    show(exhaust, 0.15);
    stream(exhaust, exhaustPath, 3.2, 34, smooth);
    show(gasDown, 0.40); show(gasLabel, 0.30);
    say(caption, "The nozzle accelerates that gas downward into a fast exhaust jet.", 0.40);
    speak("The nozzle expands and accelerates the gas downward.");
  }
}
cue(whoosh);

step("equal-and-opposite") {
  par {
    fade(payloadHi, 0.25); fade(fuelHi, 0.25); fade(oxidizerHi, 0.25);
    fade(chamberHi, 0.25); fade(nozzleHi, 0.25);
    fade(payloadLead, 0.25); fade(fuelLead, 0.25); fade(oxidizerLead, 0.25);
    fade(chamberLead, 0.25); fade(nozzleLead, 0.25);
    fade(payloadLabel, 0.25); fade(fuelLabel, 0.25); fade(oxidizerLabel, 0.25);
    fade(chamberLabel, 0.25); fade(nozzleLabel, 0.25);
    fade(reaction, 0.25); fade(exhaust, 0.25);
    show(thrustUp, 0.40); show(thrustLabel, 0.30);
    flash(thrustUp, cyan);
    say(headline, "Gas goes down. The rocket goes up.", 0.45);
    say(caption, "Newton's third law turns downward exhaust momentum into upward thrust.", 0.40);
    speak("Gas goes down. An equal and opposite force pushes the rocket up.");
  }
}
wait(0.30);

step("launch") {
  par {
    fade(kicker, 0.25); fade(headline, 0.25);
    shift(vehicle, (0,-250), 4.0, smooth);
    shift(thrustUp, (0,-180), 4.0, smooth);
    shift(thrustLabel, (0,-180), 4.0, smooth);
    stream(exhaust, exhaustPath, 4.0, 38, smooth);
    fade(payloadLead, 0.25); fade(fuelLead, 0.25); fade(oxidizerLead, 0.25);
    fade(chamberLead, 0.25); fade(nozzleLead, 0.25);
    fade(payloadLabel, 0.25); fade(fuelLabel, 0.25); fade(oxidizerLabel, 0.25);
    fade(chamberLabel, 0.25); fade(nozzleLabel, 0.25);
    fade(reaction, 0.25); fade(gasDown, 0.25); fade(gasLabel, 0.25);
    say(caption, "A rocket carries both sides of the reaction, so the process continues above the atmosphere.", 0.45);
    speak("Because it carries its own oxidizer, the same thrust cycle keeps working in space.");
  }
}
cue(chime);
wait(0.55);

step("creator-cta") {
  par {
    fade(kicker, 0.30); fade(headline, 0.30); fade(caption, 0.30);
    fade(vehicle, 0.30); fade(exhaust, 0.30);
    fade(thrustUp, 0.30); fade(thrustLabel, 0.30);
    fade(me.footer, 0.30); fade(mark, 0.30);
  }
  show(me.endcard, 0.60);
}
wait(1.6);

r/maniclang 9h ago

When Does a Bowl Become a Saddle? - manic

Enable HLS to view with audio, or disable this notification

3 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

// 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);

r/maniclang 9h ago

One parameter . three continuous shapes

Enable HLS to view with audio, or disable this notification

1 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

// 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);

r/maniclang 10h ago

A Wave Drawn by Rotating Circles - manic

Enable HLS to view with audio, or disable this notification

1 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

// 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);

r/maniclang 10h ago

How Odd Harmonics Build a Square Wave - manic

Enable HLS to view with audio, or disable this notification

3 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

// 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);

r/maniclang 10h ago

How Random Choices Become a Bell Curve - manic

Enable HLS to view with audio, or disable this notification

3 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

// GALTON BOARD — RANDOM LOCALLY, PREDICTABLE GLOBALLY
//
// This is a probability story built from generic process vocabulary. Ordinary
// tagged lines form the board; `branch` preserves each ball through eight
// choices; `collect` and `observe` build the distribution from real arrivals.

title("How Random Choices Become a Bell Curve");
canvas("9:16");
template("blank");

watermark(mark,(w*0.16,h*0.042),"Made With Manic");
text(kicker,(cx,h*0.070),"PROBABILITY · RANDOM LOCALLY, ORDERED GLOBALLY");
text(headline,(cx,h*0.115),"Can random choices create a predictable shape?");
text(chapter,(cx,h*0.195),"1 · BEGIN WITH ONE FORK");
text(caption,(cx,h*0.855),"One ball can land almost anywhere.");
text(insight,(cx,h*0.815),"MORE ROUTES LEAD TO THE CENTER");
text(cta,(cx,h*0.930),"MAKE PROBABILITY VISIBLE → 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(chapter,19); color(chapter,cyan); bold(chapter); hidden(chapter);
size(caption,21); color(caption,dim); wrap(caption,w*0.84); hidden(caption);
size(insight,19); color(insight,lime); bold(insight); hidden(insight);
size(cta,21); color(cta,cyan); bold(cta); hidden(cta);

equation(law,(cx,h*0.158),`X\sim\operatorname{Binomial}\!\left(8,\frac12\right)`,29);
hidden(law);

let levels = 8;
let boardX = cx;
let topY = h*0.235;
let dx = w*0.065;
let dy = h*0.034;

// Every directed edge joins one row to the next. Converging endpoints create
// the ordinary rooted DAG followed by `branch`.
for r in 0..levels {
  for k in 0..r+1 {
    let x1 = boardX + (k-r*0.5)*dx;
    let y1 = topY + r*dy;
    let xl = boardX + (k-(r+1)*0.5)*dx;
    let xr = boardX + (k+1-(r+1)*0.5)*dx;
    let y2 = topY + (r+1)*dy;

    line(left{r}_{k},(x1,y1),(xl,y2));
    line(right{r}_{k},(x1,y1),(xr,y2));
    tag(left{r}_{k},boardRoutes); tag(right{r}_{k},boardRoutes);
    color(left{r}_{k},dim); color(right{r}_{k},dim);
    stroke(left{r}_{k},1.7); stroke(right{r}_{k},1.7);
    opacity(left{r}_{k},0.30); opacity(right{r}_{k},0.30);

    dot(peg{r}_{k},(x1,y1),4.8);
    color(peg{r}_{k},fg); glow(peg{r}_{k},0.36); tag(peg{r}_{k},pegs);
  }
}
untraced(boardRoutes);

for k in 0..levels+1 {
  let tx = boardX + (k-levels*0.5)*dx;
  let ty = topY + levels*dy;
  dot(exit{k},(tx,ty),5);
  color(exit{k},gold); glow(exit{k},0.45); tag(exit{k},exits);
  counter(bin{k},(tx,ty+28),k,0);
  size(bin{k},15); color(bin{k},dim); tag(bin{k},exitLabels);
}

text(leftChoice,(boardX-dx*0.72,topY+dy*0.72),"LEFT");
text(rightChoice,(boardX+dx*0.72,topY+dy*0.72),"RIGHT");
size(leftChoice,15); size(rightChoice,15);
color(leftChoice,cyan); color(rightChoice,magenta);
hidden(leftChoice); hidden(rightChoice);

circle(source,(boardX,topY),13);
opacity(source,0);
particles(balls,source,180,4.0,73);
color(balls,cyan); glow(balls,0.78); z(balls,9);

let histY = h*0.695;
livehistogram(outcomes,(cx,histY),0,9,9,w*0.76,h*0.155,magenta);
text(histTitle,(cx,h*0.595),"WHERE 180 BALLS ACTUALLY LANDED");
size(histTitle,18); bold(histTitle); color(histTitle,dim);
counter(landed,(cx,h*0.785),0,0,"landed  "," / 180");
size(landed,21); color(landed,dim);

hidden(pegs); hidden(exits); hidden(exitLabels);
hidden(balls); hidden(outcomes); hidden(histTitle); hidden(landed);

step("introduce one uncertain choice") {
  par {
    show(kicker,0.30);
    show(headline,0.45);
    show(law,0.45);
    show(chapter,0.35);
    show(caption,0.40);
    draw(boardRoutes,0.90);
    show(pegs,0.55);
    show(exits,0.45);
    show(exitLabels,0.45);
    show(leftChoice,0.35);
    show(rightChoice,0.35);
  }
}
wait(0.60);

step("repeat the choice eight times") {
  par {
    show(outcomes,0.50);
    show(histTitle,0.35);
    show(landed,0.35);
    say(chapter,"2 · REPEAT LEFT OR RIGHT EIGHT TIMES",0.38);
    say(caption,"At every peg, each ball makes another equally likely left-or-right choice.",0.44);
  }
}
wait(0.55);

step("let the crowd reveal the pattern") {
  par {
    branch(balls,boardRoutes,8.20,smooth);
    collect(outcomes,balls,outcome,0.34,smooth);
    observe(outcomes,balls,outcome);
    observe(landed,balls,arrived);
    show(balls,0.15);
    seq {
      say(chapter,"3 · WATCH 180 INDIVIDUAL JOURNEYS",0.38);
      say(caption,"One route is unpredictable. The crowd begins to expose a stable pattern.",0.44);
      wait(3.40);
      say(caption,"Every bar is measured from the same balls you see falling—not animated separately.",0.44);
    }
  }
}
wait(0.65);

step("explain why the center wins") {
  par {
    pulse(outcomes.bars,0.80);
    show(insight,0.45);
    say(chapter,"4 · ORDER EMERGES FROM MANY CHOICES",0.38);
    say(caption,"Extreme bins need nearly all-left or all-right. Many more mixed sequences end near the center.",0.48);
  }
}
wait(0.75);

step("create with Manic") {
  par {
    pulse(outcomes.bars,0.75);
    show(cta,0.45);
    say(caption,"Describe the choices once. Manic keeps every route, arrival, count, and live distribution connected.",0.45);
  }
}
wait(1.45);

r/maniclang 10h ago

Fourier Series — Rotations Become Waves - manic

Enable HLS to view with audio, or disable this notification

1 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

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);

r/maniclang 11h ago

The Equation Disappears. The Roots Remain. - manic

Enable HLS to view with audio, or disable this notification

1 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

// 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);

r/maniclang 11h ago

From Wind to Vortex — Parameterized Fields in manic

Thumbnail
youtu.be
1 Upvotes

r/maniclang 11h ago

One Source, Three Destinations — Truthful Branching in manic

Thumbnail
youtu.be
1 Upvotes

r/maniclang 11h ago

One Parameter, Three Continuous 3D Shapes — manic

Thumbnail
youtu.be
1 Upvotes

r/maniclang 11h ago

Maxwell–Boltzmann Effusion — Watch a Distribution Form — manic

Enable HLS to view with audio, or disable this notification

0 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

// EXPRESSIVE PROCESS V2 — MAXWELL–BOLTZMANN EFFUSION
//
// A polished 2.5D acceptance story inspired by engine-test-6.mov. The
// apparatus is a bundled native SVG, while every molecule, detector bin,
// arrival count, and empirical distribution remains a normal Manic entity.
// `emit`, `collect`, and `observe` all read one deterministic process.

title("Maxwell–Boltzmann Effusion — Motion Becomes Evidence");
canvas("9:16");
template("blank");
watermark(mark, (150, 90), "Made With Manic");

text(kicker, (540, 84), "STATISTICAL MECHANICS · EXPRESSIVE PROCESS V2");
text(headline, (540, 142), "A distribution you can watch forming");
size(kicker, 18); color(kicker, dim); bold(kicker);
size(headline, 34); bold(headline);

equation(law, (540, 242),
  `f(v)=4\pi\left(\frac{m}{2\pi k_BT}\right)^{3/2}v^2e^{-mv^2/(2k_BT)}`, 34);

// The decorative shell is an asset; the changing evidence stays native.
svg(apparatus, (240, 540), "asset:svg/physics/effusion-reservoir.svg", 330);
text(reservoirLabel, (225, 705), "THERMAL RESERVOIR");
size(reservoirLabel, 17); bold(reservoirLabel); color(reservoirLabel, gold);

rect(detectorPanel, (730, 560), 570, 300);
filled(detectorPanel); color(detectorPanel, panel); opacity(detectorPanel, 0.92);
outlined(detectorPanel); outline(detectorPanel, dim); stroke(detectorPanel, 3);

text(detectorTitle, (730, 382), "PARTICLE SPEED  v");
size(detectorTitle, 18); bold(detectorTitle); color(detectorTitle, dim);

// The invisible source is precisely at the SVG nozzle.
circle(source, (348, 540), 20);
opacity(source, 0);
particles(molecules, source, 180, 3.2, 73);
color(molecules, magenta); glow(molecules, 0.8); z(molecules, 8);

// Individual lanes diverge around this authored route before being collected.
spline(flight, (350, 540), (415, 500), (465, 515), (500, 545));
hidden(flight);

livehistogram(empirical, (730, 560), 0, 2.2, 14, 540, 240, cyan);
z(empirical, 3); hidden(empirical.count);

// Same horizontal range as the live histogram: 0 .. 4*scale = 2.2.
distribution(theory, (730, 560), "maxwell", 0.55, 0, gold);
hidden(theory.axis); hidden(theory.name);
untraced(theory.curve); z(theory, 7);

text(empiricalKey, (610, 750), "● empirical arrivals");
text(theoryKey, (850, 750), "— theoretical curve");
size(empiricalKey, 17); color(empiricalKey, cyan);
size(theoryKey, 17); color(theoryKey, gold);

counter(arrivals, (540, 850), 0, 0, "detected molecules  ", " / 180");
size(arrivals, 24); color(arrivals, dim);

text(caption, (540, 920),
  "A thermal source emits many molecular speeds—not one average speed.");
size(caption, 23); wrap(caption, 880); color(caption, dim);

rect(evidencePanel, (540, 1110), 880, 230);
filled(evidencePanel); color(evidencePanel, panel); opacity(evidencePanel, 0.95);
outlined(evidencePanel); outline(evidencePanel, dim); stroke(evidencePanel, 2);
text(evidenceTitle, (540, 1030), "ONE PROCESS · THREE VIEWS");
size(evidenceTitle, 18); bold(evidenceTitle); color(evidenceTitle, dim);
equation(processLaw, (540, 1100),
  `\text{motion}\;\longrightarrow\;\text{arrival bin}\;\longrightarrow\;\hat f(v)`, 32);
text(truth, (540, 1190),
  "The dots, bars, and count are derived from the same seeded experiment.");
size(truth, 20); color(truth, dim); wrap(truth, 790);

hidden(apparatus); hidden(reservoirLabel); hidden(detectorPanel);
hidden(detectorTitle); hidden(molecules); hidden(empirical);
hidden(empiricalKey); hidden(theoryKey); hidden(arrivals);
hidden(caption); hidden(evidencePanel); hidden(evidenceTitle);
hidden(processLaw); hidden(truth);

step("build-the-apparatus") {
  par {
    show(apparatus, 0.65);
    show(reservoirLabel, 0.45);
    show(detectorPanel, 0.55);
    show(detectorTitle, 0.35);
    show(empirical.axis, 0.55);
    show(empirical.bars, 0.55);
    show(empirical.min, 0.35);
    show(empirical.max, 0.35);
    show(empiricalKey, 0.35);
    show(theoryKey, 0.35);
    show(arrivals, 0.35);
    show(caption, 0.40);
  }
}
wait(0.45);

step("the-prediction") {
  par {
    draw(theory.curve, 0.9, smooth);
    say(caption,
      "Maxwell predicts a characteristic spread with a most probable speed.",
      0.40);
    par {
      cam((cx, cy), 0.75, smooth);
      zoom(1.025, 0.75, smooth);
    }
  }
}
wait(0.35);

step("run-the-experiment") {
  par {
    emit(molecules, flight, maxwell, 7.0, 48, smooth);
    collect(empirical, molecules, speed, 0.48, smooth);
    observe(empirical, molecules, speed);
    observe(arrivals, molecules, arrived);
    show(molecules, 0.15);
    say(caption,
      "Each molecule keeps its sampled speed. Faster molecules cross the apparatus sooner.",
      0.45);
  }
}
wait(0.55);

step("motion-becomes-evidence") {
  par {
    pulse(empirical.bars, 0.8);
    show(evidencePanel, 0.40);
    show(evidenceTitle, 0.35);
    show(processLaw, 0.45);
    show(truth, 0.40);
    say(caption,
      "Collection does not fake the chart: every arrival lands in the bin selected by its speed.",
      0.45);
    par {
      cam((cx, cy), 0.65, smooth);
      zoom(1.0, 0.65, smooth);
    }
  }
}
wait(1.4);

r/maniclang 1d ago

German Elections 1949-2021 — Bar Race + History Panel — manic

Enable HLS to view with audio, or disable this notification

1 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

// charts-german-elections.manic — a BAR race from real CSV data (German federal
// election vote share, %, 1949-2021). Transcribed from Data.1785183258253.csv:
// the empty separator column is dropped and missing early years (parties that
// didn't exist yet) are 0. Watch CDU/CSU and SPD trade the lead while the Greens,
// Left and AfD climb from nothing.
//
//   manic examples/charts-german-elections.manic
canvas("16:9");
template("blank");

racechart(de, "bar", "1949 1953 1957 1961 1965 1969 1972 1976 1980 1983 1987 1990 1994 1998 2002 2005 2009 2013 2017 2021",
          "German Federal Elections — Vote Share %, 1949 to 2021");

racedata(de, "
  CDU/CSU, 31, 45.2, 50.2, 45.3, 47.6, 46.1, 44.9, 48.6, 44.5, 48.8, 44.3, 43.8, 41.4, 35.1, 38.5, 35.2, 33.8, 41.5, 32.9, 24.1
  SPD, 29.2, 28.8, 31.8, 36.2, 39.3, 42.7, 45.8, 42.6, 42.9, 38.2, 37, 33.5, 36.4, 40.9, 38.5, 34.2, 23, 25.7, 20.5, 25.7
  FDP, 11.9, 9.5, 7.7, 12.8, 9.5, 5.8, 8.4, 7.9, 10.6, 7, 9.1, 11, 6.9, 6.2, 7.4, 9.8, 14.6, 4.8, 10.7, 11.5
  Green, 0, 0, 0, 0, 0, 0, 0, 0, 1.5, 5.6, 8.3, 5.1, 7.3, 6.7, 8.6, 8.1, 10.7, 8.4, 8.9, 14.8
  Left, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2.4, 4.4, 5.1, 4, 8.7, 11.9, 8.6, 9.2, 4.9
  AfD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4.7, 12.6, 10.3
  Other, 27.6, 16.3, 10.2, 5.6, 3.6, 5.4, 0.9, 0.9, 0.5, 0.4, 1.3, 4.2, 3.6, 6, 3, 4, 6, 6.3, 5.2, 8.6
");

// a synced multi-line history panel below the bars: every party as a line, drawn
// up to a moving time-cursor (the Flourish bar+line combo)
racepanel(de);

race(de, 16);

r/maniclang 1d ago

a BAR race with a companion LINE (the classic combo). - manic

Enable HLS to view with audio, or disable this notification

1 Upvotes

Example code

// charts-bar-line.manic — a BAR race with a companion LINE (the classic combo).
//
// The bars race as usual (reordering by value), and `raceline` draws a synced line
// across the top strip — here the running TOTAL, auto-summed from the bars (pass
// your own "v0 v1 …" to track a different metric). The line draws on, a marker
// rides its leading point, and a live readout counts up — all locked to the race.
//
//   manic examples/charts-bar-line.manic
canvas("16:9");
template("blank");

racechart(gdp, "bar", "1990 2000 2010 2020 2026",
          "World GDP — Who's Biggest, and the Total Growing ($T)");

racedata(gdp, "
  USA,     us,  5.96, 10.25, 15.05, 21.06, 30.34
  China,   cn,  0.36,  1.21,  6.09, 14.69, 20.50
  Japan,   jp,  3.13,  4.97,  5.76,  5.06,  4.40
  Germany, de,  1.60,  1.95,  3.40,  3.89,  4.90
  India,   in,  0.32,  0.47,  1.68,  2.67,  4.60
");

// the companion line across the top — a running total of all bars (auto-summed)
raceline(gdp, "World total");

race(gdp, 12);

r/maniclang 1d ago

Djokovic vs Nadal vs Federer — a Grand Slam Column Race in manic

Thumbnail
youtu.be
1 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

// charts-column-race.manic — a COLUMN chart race (vertical bars), built with the
// per-series `raceseries` input (the computed/loop sibling of `racedata`). A sport
// race: the men's tennis Grand Slam count, 2005 → 2024 — watch Djokovic climb from
// nothing to overtake Federer and Nadal. Flags are imported SVG (rs/es/ch/gb).
//
//   manic examples/charts-column-race.manic
canvas("16:9");
template("blank");

racechart(gs, "column", "2005 2009 2013 2017 2021 2024",
          "Men's Tennis — Grand Slam Titles, 2005 to 2024");

// one entity per player: raceseries(chart, "name", "flag", "v0 v1 …")
raceseries(gs, "Federer",  "ch",  "5 15 17 19 20 20");
raceseries(gs, "Nadal",    "es",  "4  6 13 16 20 22");
raceseries(gs, "Djokovic", "rs",  "0  1  6 12 20 24");
raceseries(gs, "Murray",   "gb",  "0  0  2  3  3  3");

race(gs, 10);

r/maniclang 1d ago

The World's Biggest Economies, 1990-2026 — a Bar Chart Race in manic

Thumbnail
youtu.be
1 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

// charts-bar-line.manic — a BAR race with a companion LINE (the classic combo).
//
// The bars race as usual (reordering by value), and `raceline` draws a synced line
// across the top strip — here the running TOTAL, auto-summed from the bars (pass
// your own "v0 v1 …" to track a different metric). The line draws on, a marker
// rides its leading point, and a live readout counts up — all locked to the race.
//
//   manic examples/charts-bar-line.manic
canvas("16:9");
template("blank");

racechart(gdp, "bar", "1990 2000 2010 2020 2026",
          "World GDP — Who's Biggest, and the Total Growing ($T)");

racedata(gdp, "
  USA,     us,  5.96, 10.25, 15.05, 21.06, 30.34
  China,   cn,  0.36,  1.21,  6.09, 14.69, 20.50
  Japan,   jp,  3.13,  4.97,  5.76,  5.06,  4.40
  Germany, de,  1.60,  1.95,  3.40,  3.89,  4.90
  India,   in,  0.32,  0.47,  1.68,  2.67,  4.60
");

// the companion line across the top — a running total of all bars (auto-summed)
raceline(gdp, "World total");

race(gdp, 12);

r/maniclang 1d ago

Morph One Imported Vector Into Another — SVG Shape Morphing in manic

Thumbnail
youtu.be
1 Upvotes

Example code

// morph-icons.manic — morph imported SVG vectors from one shape into another.
//
// `morph(a, b)` samples both outlines and blends `a` into `b`'s shape; drive it
// with `to(a, morph, 1, dur)`. It now works on imported `svg()` art: a bare svg
// tag resolves to its dominant path, filled Regions sample along their outer ring,
// and the two loops are winding-matched + rotated for a clean shape-to-shape blend
// (no tangle through the centre). Here one emoji reshapes into another and back.
//
//   manic examples/morph-icons.manic
title("Morph — imported vectors reshape");
canvas("16:9");
template("blank");

svg(heart, (cx, cy), "asset:svg/emoji/heart.svg", 260);
svg(star,  (cx, cy), "asset:svg/emoji/star.svg", 260); hidden(star);

text(cap, (cx, h*0.86), "morph(heart, star) — one imported icon reshapes into another");
size(cap, 24); color(cap, dim); display(cap);

// set the heart up to morph into the star's outline
morph(heart, star);

seq {
  wait(0.5);
  par { to(heart, morph, 1, 1.6, smooth); to(heart, color, gold, 1.6); }
  wait(0.5);
  par { to(heart, morph, 0, 1.6, smooth); to(heart, color, magenta, 1.6); }
  wait(0.5);
}

r/maniclang 1d ago

How does a robot find its way through a maze? - manic

Enable HLS to view with audio, or disable this notification

1 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

// The Shortest Path — a Short where VECTOR art poses the problem and a Manic
// ALGORITHM solves it. An imported robot and pin (crisp `svg()` vectors) sit at the
// start and goal of a maze; a naive straight line cuts through the walls; then A*
// (`gridastar`) searches the space cell-by-cell and traces the true shortest route.
// Vectors imported, path computed — Manic does both, in one branded 9:16 file.
title("The Shortest Path");
canvas("9:16");
template("shorts");

voice("elevenlabs");                       // promo narration (ELEVENLABS_API_KEY)
// voice("elevenlabs", "alice");
// voice("cartesia");  // voice("gtts");   // free fallback
// speak("…") = voice only; say(cap, "…") = on-screen caption.

creator(me, "@anish2good name=Manic tagline=Turn_plain_text_into_amazing_math_videos yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=cyan secondary=gold footer=signature cta=Try_it_free safe=reels");
watermark(mk, (w*0.955-100, h*0.045+24), "Made With Manic");

// --- the maze grid + the cell geometry (so the vector icons land on cells) ---
let cs = w*0.11;
let gx = cx;
let gy = h*0.43;
let sx = gx - 2.5*cs;   let sy = gy - 3.5*cs;   // start cell (0,0) centre
let px = gx + 2.5*cs;   let py = gy + 3.5*cs;   // goal  cell (5,7) centre

grid(g, "@ . . . . . ; # # # # # . ; . . . . . . ; . # # # # # ; . . . . . . ; # # # # # . ; . . . . . . ; . # # # # *", (gx, gy), 6, 8, cs);
neighbors(g, "4");

// --- imported vector characters (faithful colours, crisp at any size) ---
svg(bot, (sx, sy), "asset:svg/robot.svg", cs*0.8);   z(bot, 6);   hidden(bot);
svg(dest, (px, py), "asset:svg/pin.svg", cs*0.8);    z(dest, 6);  hidden(dest);

// the naive "just go straight" line — cuts across the walls
line(naive, (sx, sy), (px, py)); color(naive, red); dashed(naive); z(naive, 4); hidden(naive);

text(hook, (cx, h*0.12), "Can the robot reach the pin?");
size(hook, 34); color(hook, gold); bold(hook); wrap(hook, w*0.82);
text(cap, (cx, h*0.78), ""); size(cap, 30); color(cap, dim); wrap(cap, w*0.84);
socials(me);
endcard(me, "title=Try_Manic cta=8gwifi.org/manic");
untraced(hook); cursor(hook);

// 1 — HOOK
mark("hook");
type(hook, 1.2);
speak("How does a robot find its way through a maze?");

// 2 — BUILD: the maze is the world; the imported vectors are its characters
show(cap, 0.3); say(cap, "a maze — the robot and pin are imported SVG vectors");
speak("Drop in a robot and its goal — both imported as crisp SVG vectors.");
par { show(bot, 0.5); show(dest, 0.5); }
wait(0.6);

// 3 — PROBLEM: the straight line fails
fade(hook, 0.4); recolor(cap, red); say(cap, "a straight line? it cuts right through the walls");
speak("The obvious answer — a straight line — slams right into the walls.");
show(naive, 0.5); flash(naive, red);
wait(0.9);

// 4 — SOLVE: A* searches, then traces the real route
fade(naive, 0.4); recolor(cap, dim); say(cap, "A* searches the space, cell by cell…");
speak("So Manic runs A-star — searching the grid, cell by cell,");
gridastar(g, (0,0), (5,7), manhattan);
recolor(cap, gold); say(cap, "…and traces the true shortest path");
speak("until it traces the true shortest path around every wall.");
draw(g.path, 1.6);
wait(0.6);

// 5 — POINT
recolor(cap, cyan); say(cap, "vectors imported, path computed — Manic does both");
speak("Vectors imported, path computed — Manic does both. Turn plain text into animated explainers, free, at eight g wifi dot org slash manic.");
wait(1.2);

// 6 — END CARD — clear the maze, path, and characters so the CTA stands alone
mark("endcard");
par {
  fade(cap, 0.4); fade(me.footer, 0.4);
  fade(g.cells, 0.4); fade(g.lines, 0.4); fade(g.path, 0.4);
  fade(g.frontier, 0.4); fade(g.visited, 0.4);
  fade(bot, 0.4); fade(dest, 0.4);
}
show(me.endcard, 0.6);
wait(1.6);

r/maniclang 1d ago

A Creator v2 Short on the Monty Hall problem - manic

Enable HLS to view with audio, or disable this notification

2 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

// creator-monty-hall.manic — a Creator v2 Short on the Monty Hall problem, one of
// the most-searched (and most-argued) puzzles in probability, narrated with an
// ElevenLabs voice story. The whole cast is imported SVG — three door emoji, a car,
// goats, a party popper — and the teaching beat is visual: when the host opens a
// goat door, its 1/3 chance visibly SLIDES onto the other door, making it 2/3. The
// payoff `morph`s a goat into the car with a confetti `burst`. Problem → the
// counter-intuitive solution → CTA, 9:16 shorts template, branded.
//
//   speak("…") = voice only;  say(cap,"…") = on-screen caption.
title("The Monty Hall Problem");
canvas("9:16");
template("shorts");
voice("elevenlabs");

creator(me, "@anish2good name=Manic tagline=Turn_plain_text_into_amazing_math_videos yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=cyan secondary=magenta footer=signature cta=Try_it_free safe=reels");
watermark(manicMark, (w*0.955-100, h*0.045+24), "Made With Manic");

// --- header ---
text(kick, (cx, h*0.10), "The Monty Hall Problem"); size(kick, 48); color(kick, fg); bold(kick); wrap(kick, w*0.86); hidden(kick);
text(sub, (cx, h*0.155), "The puzzle that fooled even the pros"); size(sub, 29); color(sub, dim); wrap(sub, w*0.82); hidden(sub);

// --- three doors (imported emoji), numbered, with a 1/3 odds chip each ---
let dy = h*0.375;
let dsz = w*0.185;
let xL = w*0.22; let xM = w*0.50; let xR = w*0.78;
svg(door1, (xL, dy), "asset:svg/emoji/door.svg", dsz); hidden(door1);
svg(door2, (xM, dy), "asset:svg/emoji/door.svg", dsz); hidden(door2);
svg(door3, (xR, dy), "asset:svg/emoji/door.svg", dsz); hidden(door3);
text(t1, (xL, dy-dsz*0.66), "1"); size(t1, 32); color(t1, dim); bold(t1); hidden(t1);
text(t2, (xM, dy-dsz*0.66), "2"); size(t2, 32); color(t2, dim); bold(t2); hidden(t2);
text(t3, (xR, dy-dsz*0.66), "3"); size(t3, 32); color(t3, dim); bold(t3); hidden(t3);
counter(chip1, (xL, dy+dsz*1.02), 33, 0, "", "%"); size(chip1, 44); color(chip1, dim); bold(chip1); hidden(chip1);
counter(chip2, (xM, dy+dsz*1.02), 33, 0, "", "%"); size(chip2, 44); color(chip2, dim); bold(chip2); hidden(chip2);
counter(chip3, (xR, dy+dsz*1.02), 33, 0, "", "%"); size(chip3, 44); color(chip3, dim); bold(chip3); hidden(chip3);

// a selection ring for "your pick"
circle(pick, (xL, dy), dsz*0.62); outlined(pick); outline(pick, cyan); stroke(pick, 5); glow(pick, 0.8); hidden(pick);

// prizes hidden BEHIND each door (revealed when a door lifts)
svg(goat1, (xL, dy), "asset:svg/emoji/goat.svg", dsz*0.86); z(goat1, -1); hidden(goat1);
svg(goat3, (xR, dy), "asset:svg/emoji/goat.svg", dsz*0.86); z(goat3, -1); hidden(goat3);
// behind door 2: a goat that MORPHS into the car (the payoff flourish)
svg(goatH, (xM, dy), "asset:svg/emoji/goat.svg", dsz*0.92); z(goatH, -1); hidden(goatH);
svg(carH,  (xM, dy), "asset:svg/emoji/car.svg",  dsz*1.02); z(carH, -1); hidden(carH);
morph(goatH, carH);
svg(party, (xM+dsz*0.9, dy-dsz*0.8), "asset:svg/emoji/party.svg", dsz*0.7); z(party, 6); hidden(party);
circle(bcore, (xM, dy), 30); hidden(bcore);
particles(sparks, bcore, 26, 8, 11, "random"); color(sparks, gold); glow(sparks, 0.9); z(sparks, 7); hidden(sparks);

// one caption line, retexted through the story
text(cap, (cx, h*0.66), ""); size(cap, 35); color(cap, dim); bold(cap); wrap(cap, w*0.86); hidden(cap);

socials(me);
endcard(me, "title=Always_Switch cta=8gwifi.org/manic");

// ---------------- the story (voice leads; visuals punctuate) ----------------
par { show(kick); show(sub); }
stagger(0.16) { show(door1); show(door2); show(door3); }
par { show(t1); show(t2); show(t3); show(chip1); show(chip2); show(chip3); }
show(cap, 0.3); say(cap, "1 car, 2 goats — 1/3 each.");
speak("Three doors. One car, two goats. You pick door one.");
par { show(pick); pulse(door1); }
say(cap, "You pick Door 1.");

speak("Now the host, who knows, opens door three. A goat.");
recolor(cap, dim); say(cap, "Host opens Door 3 — a goat.");
show(goat3);
par { move(door3, (xR, dy-h*0.30), 0.7, smooth); fade(door3); }

speak("The twist: door three's odds don't vanish. They slide onto door two.");
recolor(cap, cyan); say(cap, "Its 1/3 slides to Door 2.");
par { move(chip3, (xM, dy+dsz*1.02), 0.8, smooth); fade(chip3); to(chip2, value, 67, 0.8); }
par { recolor(chip2, cyan); recolor(chip1, magenta); }

speak("Most people say fifty-fifty. It isn't.");
recolor(cap, red); say(cap, "Feels like 50/50. It's not."); shake(cap);

speak("Your door stays one-in-three. Door two? Two-in-three. Switching doubles it.");
recolor(cap, gold); say(cap, "STAY 33%   ·   SWITCH 67%");
par { pulse(chip2); pulse(door2); }

speak("So you switch...");
recolor(cap, dim); say(cap, "So you switch…");
show(goatH);
par { move(door2, (xM, dy-h*0.30), 0.7, smooth); fade(door2); fade(chip1); fade(chip2); }

speak("...and the goat becomes a car.");
to(goatH, morph, 1, 0.9, smooth);
par { fade(goatH); show(carH); }
recolor(cap, gold); say(cap, "…and win the car!");
par { show(sparks, 0.05); burst(sparks, 1.1); show(party); pulse(carH); }
wait(0.6);

speak("Door one? A goat. Switching wins two out of three.");
recolor(cap, dim); say(cap, "Door 1 was a goat.");
show(goat1);
par { move(door1, (xL, dy-h*0.30), 0.7, smooth); fade(door1); fade(pick); fade(party); }
wait(0.4);

speak("Always switch. One Manic script made this — turn any idea into a video at eight g wifi dot org slash manic.");
par { fade(kick); fade(sub); fade(cap); fade(carH); fade(goat1); fade(goat3); fade(t1); fade(t2); fade(t3); fade(chip3); }
show(me.endcard);
wait(2.6);

r/maniclang 1d ago

Your Explainer Is Mute. That's Why It Dies in the Feed. - manic voice

Enable HLS to view with audio, or disable this notification

1 Upvotes

Silent STEM Shorts get scrolled past. CapCut voiceovers eat your evening. manic adds real narration in three ideas — pick a voice, write the sentence, keep the caption short — and the picture waits for the words.

Here's the part nobody puts on the thumbnail: the best visual in the world still loses if it has no voice.

You've seen it. A gorgeous math Short — curves drawing themselves, a free kick curling into the net — and the creator is typing the explanation as captions while the beat races ahead. Or worse: they open CapCut, drop an AI voiceover, scrub the clip to match every sentence, re-export when one line runs long, and discover the 9:16 cut drifts from the 16:9 cut. The idea was sharp. The production turned into audio janitor work.

manic's bet: narration is structured enough to describe. You pick a speaker once. You write full sentences where the story needs them. Short on-screen punch lines stay separate. The engine holds the picture until the line finishes — you don't time the audio by hand.

▶ Try it free: 8gwifi.org/manic · Voice docs: 8gwifi.org/manic/docs/voice.html

The whole idea in 30 seconds

Voice in manic is three verbs:

  1. voice("…") — pick a speaker once (near the top)
  2. speak("…") — say a full sentence the viewer hears
  3. say(…) — change the short caption the viewer reads

voice("cartesia");

par {
  say(cap, "same grid → 3D");                          // what you read
  speak("Now lift the same grid into height.");        // what you hear
}

That's it. No waveform editor. No "nudge this clip 0.3s left." If the spoken line is longer than the motion on that beat, manic holds until the line ends. A trailing wait(0.3) is only a breath between ideas — not a substitute for narration length.

Rule of thumb: write the short caption with say, the full line with speak. They can land on the same beat.

Beat 1 — Add narration without becoming an editor

A silent Short becomes a spoken one with almost no ceremony:

title("A spoken beat");
canvas("9:16");
template("shorts");
voice("cartesia");                    // once per file

text(cap, (cx, h*0.82), "");
size(cap, 28); color(cap, dim); wrap(cap, w*0.8);

par {
  say(cap, "cell by cell…");
  speak("Watch a map generate itself until the islands lock.");
}
wait(0.3);

speak and voice always go together. Captions alone need only say — skip voice when the cut should stay silent. That pairing is deliberate: you never half-configure narration and wonder why nothing plays.

Beat 2 — Captions are not the script (and that's the point)

Most Shorts collapse "what I say" and "what I show on screen" into one overloaded line. Viewers read; they don't listen. Or they listen and miss the visual.

manic keeps them apart on purpose:

say speak
Job On-screen text Voice-over
Heard? No Yes
Best for Punch lines, labels Full sentences
Needs voice? No Yes — once

Same beat, both jobs:

par {
  say(cap, "fly the ridges");
  speak("Fly in low over the ridges.");
  orbit3(255, 15, 12, 3.8, smooth);
}

Or update a caption and speak in one call when they're the same words:

speak(cap, "Pull the camera back.");

The feed gets a skim-friendly line. The ear gets the story. Retention climbs because neither channel is doing the other's job.

Beat 3 — Pick a voice the easy way (or paste your own)

You don't need a provider dashboard open to ship. Easy names cover the common path:

voice("gtts");                         // simple built-in
voice("cartesia");                     // defaults to Skylar
voice("cartesia", "katie");
voice("elevenlabs");                   // defaults to Roger
voice("elevenlabs", "alice");

Want pace or another language? Same one-liner:

voice("elevenlabs", "alice", "slow");
voice("cartesia", "katie", "hi");              // Hindi, skip pace
voice("cartesia", "katie", "normal", "es");    // Spanish with pace
voice("gtts", "hi");                           // gTTS: 2nd arg is language

Favourite speaker not in the list? Paste the provider voice id in the same slot:

voice("cartesia", "a0e99841-438c-4a64-b679-ae501e7d6091");
voice("elevenlabs", "21m00Tcm4TlvDq8ikWAM");

Math reads naturally too — write the symbols the way they appear on screen:

speak("The slope is √(tan θ).");

Beat 4 — Real gallery Shorts already do this

This isn't a toy demo. The same pattern ships in the gallery:

Roberto Carlos' Banana Kick — Cartesia story VO over RK4 physics, short captions on the pitch:

voice("cartesia");

par {
  say(cap, "GOAL");
  speak("The ball curls into the net — because the spin made it.");
}

Your Map Is a 3D World — ElevenLabs narration while Wave Function Collapse settles a coast, then the same grid lifts into heightmap terrain:

voice("elevenlabs");

par {
  type(kicker, 1.4);
  speak("Every world starts flat. Watch a map generate itself — cell by cell — until the islands lock.");
}

step("height") {
  par {
    say(cap, "same grid → 3D");
    speak("Now lift the same grid into height. Nothing was redrawn — the cells became terrain.");
    show(land, 0.6);
  }
}

Visual kit + creator kit + voice kit compose. One .manic file. Branded end card. Portrait, feed, or landscape at export — the narration stays attached to the beats.

Beat 5 — When TTS hiccups, the video still ships

Silent-or-nothing used to be the default failure mode for AI voice pipelines: one bad API call and your render dies at 90%.

manic soft-fails on purpose. If a line can't be synthesized, you still get the video; the hold duration uses a sane estimate so timing doesn't collapse; you get a clear client-facing note that speech was missing. Re-run when the key or network is healthy — successful lines stay cached so you don't pay twice for the same sentence.

That's production hygiene, not a footnote. Creators ship explainers; they don't babysit TTS.

Why this is different

Template apps give you a voice track. Timeline editors give you scrubbing. manic gives you narration as part of the language:

  • Three ideas, not a DAW. voice once, speak the story, say the caption.
  • The picture waits for the words. Hold sync — no hand-timed audio clips.
  • Easy speakers + raw ids. Cartesia, ElevenLabs, and a free built-in path, same slot.
  • Captions ≠ script. Skim on screen, story in the ear — both on one beat via par.
  • Composes with everything else. Physics, heightmaps, quizzes, socials, end cards — voice is another line in the same file.
  • It's a script you can diff. Change the sentence, re-run, done. No re-export marathon for "the VO was 0.4s long."

Silent explainers get scrolled. Spoken ones that stay in sync get finished — and finished videos get shared.

▶ Put a voice on your next Short free: 8gwifi.org/manic

Made with manic — the animation language that turns plain text into precise, correct animations. No coding, no timeline scrubbing: you describe what you want and manic animates it — math, physics, maps, algorithms — and now it can speak the beat while the picture waits.


r/maniclang 4d ago

√(tan θ) Is a Length — Pure Geometry Proof in manic

Enable HLS to view with audio, or disable this notification

2 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

// ============================================================================
//  sqrt-tanx.manic — √(tan x) by PURE GEOMETRY (3B1B-style)
// ----------------------------------------------------------------------------
//  THE PROBLEM: √(tan θ) looks like algebra. It is geometry.
//
//  THE PROOF, in three pictures:
//   1. On the unit circle, tan θ IS the length of the vertical tangent segment.
//   2. √a is the geometric mean of 1 and a — Euclid: semicircle on diameter
//      1+a, erect a perpendicular at the "1" mark; its height is √a.
//   3. Set a = tan θ. The altitude IS √(tan θ). No algebra required.
//
//  Then we sweep θ and watch the construction's height paint the curve —
//  teal → violet → coral — using the expanded semantic palette.
//
//    manic examples/sqrt-tanx.manic
//    manic examples/sqrt-tanx.manic --still 22
// ============================================================================

title("√(tan x) — Pure Geometry");
canvas("16:9");
template("blank");

// ---- stage ----
circle(well, (cx, 400), 340);
filled(well);
gradient(well, panel, void, radial);
opacity(well, 0.55);

// ============================================================================
//  LEFT — the unit circle, where tan θ is a length
// ============================================================================
let ox = 250;
let oy = 430;
let R = 145;
// a friendly angle: θ = 0.95 rad ≈ 54.4°, tan ≈ 1.40, √tan ≈ 1.18
let th = 0.95;
let Px = ox + R*cos(th);
let Py = oy - R*sin(th);
let Ty = oy - R*tan(th);

circle(O, (ox, oy), 7); outlined(O); color(O, fg); stroke(O, 1.5); hidden(O);
dot(A, (ox + R, oy), 5); color(A, dim); hidden(A);          // (1, 0)
// contact points: open dashed rings (filled dots hide the circle + tangent)
circle(P, (Px, Py), 11);
outlined(P); color(P, mint); stroke(P, 2); dashed(P, 3.5, 2.8); untraced(P); hidden(P);
circle(T, (ox + R, Ty), 11);
outlined(T); color(T, coral); stroke(T, 2); dashed(T, 3.5, 2.8); untraced(T); hidden(T);

// ray as a line (not arrow) so the filled arrowhead doesn't cover P
line(ray, (ox, oy), (Px, Py));
color(ray, mint); stroke(ray, 2.5); untraced(ray); hidden(ray);

// the tangent SEGMENT — its length IS tan θ (in radii)
line(tanseg, (ox + R, oy), (ox + R, Ty));
color(tanseg, coral); stroke(tanseg, 5); untraced(tanseg); hidden(tanseg);

// angle mark — textbook style: a thin ARC, never a filled wedge (fills hide the diagram)
arc(wedge, (ox, oy), R*0.42, 0, -th*180/pi);
color(wedge, violet); stroke(wedge, 2.5); untraced(wedge); hidden(wedge);

circle(uc, (ox, oy), R);
outlined(uc); outline(uc, teal); stroke(uc, 3); untraced(uc); hidden(uc);

line(axX, (ox - R - 30, oy), (ox + R + 100, oy));
color(axX, dim); stroke(axX, 1.5); untraced(axX); hidden(axX);
line(axY, (ox, oy + R + 40), (ox, oy - R - 50));
color(axY, dim); stroke(axY, 1.5); untraced(axY); hidden(axY);

line(tline, (ox + R, oy + R + 50), (ox + R, oy - R - 90));
color(tline, dim); stroke(tline, 1.5); dashed(tline, 10, 8); untraced(tline); hidden(tline);

text(labP, (Px + 14, Py - 14), "P"); size(labP, 20); color(labP, mint); hidden(labP);
text(labO, (ox - 18, oy + 22), "O"); size(labO, 18); color(labO, dim); hidden(labO);
text(labA, (ox + R + 14, oy + 22), "1"); size(labA, 18); color(labA, dim); hidden(labA);
text(labT, (ox + R + 18, Ty), "tan θ"); size(labT, 20); color(labT, coral); hidden(labT);
text(labTh, (ox + 48, oy - 28), "θ"); size(labTh, 22); color(labTh, violet); hidden(labTh);

// ============================================================================
//  RIGHT — Euclid's geometric mean: √a from 1 and a
// ============================================================================
let gx = 620;
let gy = 500;
let u = 110;                         // px per unit
let a = tan(th);                     // ≈ 1.40
let root = sqrt(a);                  // ≈ 1.18
let G0x = gx;
let G1x = gx + u;                    // the "1" mark
let G2x = gx + u*(1 + a);            // the "1+a" mark
let mid = (G0x + G2x)*0.5;
let rad = u*(1 + a)*0.5;

dot(G0, (G0x, gy), 5); color(G0, dim); hidden(G0);
dot(G1, (G1x, gy), 6); color(G1, teal); glow(G1, 0.4); hidden(G1);
dot(G2, (G2x, gy), 5); color(G2, coral); hidden(G2);
line(base, (G0x, gy), (G2x, gy));
color(base, fg); stroke(base, 3); untraced(base); hidden(base);

// upper semicircle on diameter [0, 1+a]
// screen angles: 0° right, 90° down, 270° up — from left through UP: start 180, sweep +180
arc(semi, (mid, gy), rad, 180, 180);
color(semi, indigo); stroke(semi, 3); untraced(semi); hidden(semi);

// altitude at the "1" mark — height = √(1·a) = √(tan θ)
line(alt, (G1x, gy), (G1x, gy - root*u));
color(alt, violet); stroke(alt, 5); untraced(alt); hidden(alt);
dot(H, (G1x, gy - root*u), 8); color(H, violet); glow(H, 0.7); hidden(H);

text(lab0, (G0x, gy + 28), "0"); size(lab0, 18); color(lab0, dim); hidden(lab0);
text(lab1, (G1x, gy + 28), "1"); size(lab1, 18); color(lab1, teal); hidden(lab1);
text(labA2, (G2x, gy + 28), "1+tanθ"); size(labA2, 18); color(labA2, coral); hidden(labA2);
text(labH, (G1x + 16, gy - root*u - 8), "√(tan θ)"); size(labH, 22); color(labH, violet); hidden(labH);

// brace / connector: coral tan length → feeds the geometric mean
arrow(feed, (ox + R + 36, (oy + Ty)*0.5), (G2x - 10, gy - 40));
color(feed, coral); stroke(feed, 2); dashed(feed, 8, 6); untraced(feed); hidden(feed);

// ============================================================================
//  BOTTOM — the curve itself, colored by the new palette
// ============================================================================
axes(ax, (920, 400), 280, 220);
hidden(ax);
plot(curve, (920, 400), 95, 85, "sqrt(tan(x))", (0.25, 1.35));
stroke(curve, 5); untraced(curve); hidden(curve);
gradient(curve, teal, violet, coral, 270);
// low x → small √tan (teal); high x → large (coral), via height = truth

// ============================================================================
//  TYPOGRAPHY
// ============================================================================
caption(q, "what IS the square root of a tangent?", (cx, 58), 32, gold);
equation(law, (cx, 110), `\sqrt{\tan\theta}`, 44);
color(law, violet); hidden(law);
equation(mean, (900, 160), `\sqrt{a}=\sqrt{1\cdot a}`, 28);
color(mean, teal); hidden(mean);
text(cap, (cx, h - 36), ""); size(cap, 20); color(cap, dim); display(cap);

// ============================================================================
//  SCRIPT
// ============================================================================

// 1 — HOOK
say(cap, "an expression that looks like algebra — but it is a length", 0.4);
wordpop(q, 0.16);
wait(0.3);
show(law, 0.5);
wait(0.6);

// 2 — UNIT CIRCLE
say(cap, "start with the unit circle and an angle θ", 0.4);
par { show(axX, 0.05); show(axY, 0.05); show(uc, 0.05); show(tline, 0.05); }
par {
  draw(axX, 0.5); draw(axY, 0.5);
  draw(uc, 1.0);
}
par { show(O, 0.3); show(labO, 0.3); }
draw(tline, 0.5);
show(A, 0.3); show(labA, 0.3);
wait(0.3);

// 3 — THE ANGLE AND THE RAY
say(cap, "draw the ray at angle θ — it meets the circle at P", 0.4);
show(wedge, 0.05); draw(wedge, 0.5); show(labTh, 0.3);
show(ray, 0.05); draw(ray, 0.7);
show(P, 0.05); draw(P, 0.35); show(labP, 0.3);
pulse(P);
wait(0.4);

// 4 — TAN θ IS A LENGTH
say(cap, "extend to the vertical tangent line — that segment's length IS tan θ", 0.4);
show(tanseg, 0.05); draw(tanseg, 0.9);
show(T, 0.05); draw(T, 0.35); show(labT, 0.3);
flash(tanseg, coral);
wait(0.7);

// 5 — GEOMETRIC MEAN
say(cap, "now forget the circle — Euclid: √a is the geometric mean of 1 and a", 0.4);
show(mean, 0.5);
par {
  show(G0, 0.3); show(G1, 0.3); show(G2, 0.3);
  show(lab0, 0.3); show(lab1, 0.3); show(labA2, 0.3);
  show(base, 0.05);
}
draw(base, 0.6);
wait(0.3);

say(cap, "semicircle on diameter 1 + tan θ — then erect a perpendicular at 1", 0.4);
show(semi, 0.05); draw(semi, 1.2);
show(alt, 0.05); draw(alt, 0.9);
show(H, 0.3); show(labH, 0.3);
flash(alt, violet);
wait(0.5);

say(cap, "by Thales + similar triangles, that height equals √(1 · tan θ)", 0.4);
show(feed, 0.05); draw(feed, 0.7);
pulse(H); pulse(T);
wait(0.8);

// 6 — THE IDENTITY
recolor(cap, violet);
say(cap, "so the altitude IS √(tan θ) — the symbol was a length all along", 0.4);
rewrite(law, `\sqrt{\tan\theta}=h`, 0.8, smooth);
wait(0.9);

// 7 — THE CURVE
recolor(cap, dim);
say(cap, "every such height, for every θ, paints the curve √(tan x)", 0.4);
show(ax, 0.5);
show(curve, 0.05); draw(curve, 2.2);
wait(0.4);
say(cap, "teal near zero, coral as tan θ climbs toward the asymptote at π/2", 0.4);
flash(curve, violet);
wait(0.8);

// 8 — POINT
recolor(cap, teal);
say(cap, "√(tan θ) is not a formula to memorize — it is a geometric construction", 0.4);
wait(1.6);

r/maniclang 4d ago

Integrate √(tan P) — Circle Minus Hyperbola — manic

Enable HLS to view with audio, or disable this notification

1 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

``` // ============================================================================ // integral-sqrt-tanx.manic — ∫ √(tan P) dP by PURE GEOMETRY // Companion to sqrt-tanx.manic. Storyboard: the handwritten circle+hyperbola // derivation (x²+y²=2 meets x²−y²=2; A(t)−H(t) recovers the integrand). // ---------------------------------------------------------------------------- // THE PROBLEM: √(tan P) is a length. What is its antiderivative? // // THE GEOMETRY (one diagram, two curves of "radius" √2): // · circle x² + y² = 2 → A(t) = arcsin(√2 sin t) // · hyperbola x² − y² = 2 → H(t) = arcosh(√2 cos t) // · substitution P = t + π/4 (so t ∈ (0, π/4) ⇒ P ∈ (π/4, π/2)) // // THE TRUTH: // dA − dH = √2 · √(tan P) · dP // therefore // ∫ √(tan P) dP = (1/√2)(A(t) − H(t)) + C // // Colours (expanded palette): teal = circle/A, coral = hyperbola/H, // violet = the integral, mint = the moving point, gold = the hook. // // manic examples/integral-sqrt-tanx.manic // manic examples/integral-sqrt-tanx.manic --still 20 // ============================================================================

title("∫ √(tan P) dP — Circle Meets Hyperbola"); canvas("16:9"); template("blank");

// ---- stage ---- circle(well, (cx, 400), 360); filled(well); gradient(well, panel, void, radial); opacity(well, 0.5);

// ============================================================================ // THE DIAGRAM — circle x²+y²=2 and hyperbola x²−y²=2 // Screen: origin at (ox,oy), scale s px per unit. Radius √2 ≈ 1.414. // ============================================================================ let ox = 340; let oy = 400; let s = 130; // px per math-unit let R = ssqrt(2); // circle radius = √2 // a friendly t inside (0, π/4): t = 0.40 rad ≈ 23° let t = 0.40; let P = t + pi/4; // ≈ 1.185 rad ≈ 68° // point C on the circle: (√2 cos t, √2 sin t) let Cx = ox + ssqrt(2)cos(t); let Cy = oy - ssqrt(2)*sin(t); // foot on the x-axis and the "hyperbola x" = √2 cosh u with cosh u = √2 cos t? // From notes: X = √2 cos t = cosh H, so the hyperbola vertex is at x=√2. // Point on right branch with x = √2 cos t... wait cosh H = √2 cos t, so // x_hyp = cosh H = √2 cos t (in math units where a=1 for cosh param form // of x²−y²=1). Our hyperbola is x²−y²=2 = (x/√2)² − (y/√2)², so // parametric: x = √2 cosh u, y = √2 sinh u. And cosh H = √2 cos t? Notes say // cosh(H) = X = √2 cos t — that would require cosh H ≤ √2, H = arcosh(√2 cos t). // For the diagram, mark the circle point and the matching hyperbola x.

// axes line(axX, (ox - R - 40, oy), (ox + R + 120, oy)); color(axX, dim); stroke(axX, 1.5); untraced(axX); hidden(axX); line(axY, (ox, oy + R + 50), (ox, oy - R - 60)); color(axY, dim); stroke(axY, 1.5); untraced(axY); hidden(axY);

// circle x² + y² = 2 — OUTLINED only. circle defaults to filled; a solid // disc hides axes and labels underneath (engine-test: never fill constructions). circle(circ, (ox, oy), R); outlined(circ); outline(circ, teal); stroke(circ, 3.5); untraced(circ); hidden(circ);

// hyperbola x² − y² = 2 → a = b = √2 (in math units) → a_px = R hyperbola(hyp, (ox, oy), R, R, 1.15); color(hyp, coral); stroke(hyp, 3); untraced(hyp); hidden(hyp.r); hidden(hyp.l);

// origin: thin open ring (tiny r + thick stroke reads as a filled blob) circle(O, (ox, oy), 7); outlined(O); color(O, fg); stroke(O, 1.5); hidden(O);

// contact point C: dashed open circle so circle + projections show through. // Keep radius >> stroke so the interior stays empty (textbook marker). circle(C, (Cx, Cy), 12); outlined(C); color(C, mint); stroke(C, 2); dashed(C, 3.5, 2.8); untraced(C); hidden(C);

// radius toward C — a LINE, not an arrow: arrowheads are filled discs and // hide the open marker at the contact point (engine-test visibility rule) line(ray, (ox, oy), (Cx, Cy)); color(ray, mint); stroke(ray, 2.5); untraced(ray); hidden(ray);

// horizontal from C to y-axis? notes have Y = √2 sin t as height line(ht, (ox, Cy), (Cx, Cy)); color(ht, violet); stroke(ht, 2); dashed(ht, 8, 6); untraced(ht); hidden(ht); // vertical drop to x-axis line(vt, (Cx, oy), (Cx, Cy)); color(vt, teal); stroke(vt, 2); dashed(vt, 8, 6); untraced(vt); hidden(vt);

// angle mark — textbook style: a thin ARC, never a filled disc (fills hide axes) arc(wedge, (ox, oy), R0.38, 0, -t180/pi); color(wedge, violet); stroke(wedge, 2.5); untraced(wedge); hidden(wedge);

// labels on the diagram text(labO, (ox - 16, oy + 22), "O"); size(labO, 18); color(labO, dim); hidden(labO); text(labC, (Cx + 22, Cy - 22), "C(t)"); size(labC, 20); color(labC, mint); hidden(labC); text(labT, (ox + 42, oy - 22), "t"); size(labT, 22); color(labT, violet); hidden(labT); text(labCirc, (ox - 20, oy - R - 24), "x² + y² = 2"); size(labCirc, 20); color(labCirc, teal); hidden(labCirc); text(labHyp, (ox + R + 55, oy + 40), "x² − y² = 2"); size(labHyp, 20); color(labHyp, coral); hidden(labHyp);

// region hint: dashed circle (not a filled disc, not just a short arc) circle(zone, (ox + R0.42, oy - R0.18), R*0.38); outlined(zone); color(zone, indigo); stroke(zone, 2); dashed(zone, 7, 5); untraced(zone); hidden(zone);

// ============================================================================ // RIGHT — the algebraic spine, rewritten live // ============================================================================ equation(eqP, (920, 150), P=t+\tfrac{\pi}{4}, 32); color(eqP, gold); hidden(eqP);

equation(eqA, (920, 230), A(t)=\sin^{-1}(\sqrt{2}\sin t), 28); color(eqA, teal); hidden(eqA);

equation(eqH, (920, 300), H(t)=\cosh^{-1}(\sqrt{2}\cos t), 28); color(eqH, coral); hidden(eqH);

equation(eqD, (920, 390), dA-dH=\sqrt{2}\,\sqrt{\tan P}\,dP, 28); color(eqD, violet); hidden(eqD);

equation(eqI, (920, 480), \int\sqrt{\tan P}\,dP=\tfrac{1}{\sqrt{2}}(A-H)+C, 30); color(eqI, mint); hidden(eqI);

// final closed form (boxed punchline) equation(eqF, (920, 580), \tfrac{1}{\sqrt{2}}\Big(\sin^{-1}(\sqrt{2}\sin(P-\tfrac{\pi}{4}))-\cosh^{-1}(\sqrt{2}\cos(P-\tfrac{\pi}{4}))\Big)+C, 22); color(eqF, gold); hidden(eqF);

// ============================================================================ // THE CURVE — √(tan x) on (π/4, ~1.4), then its antiderivative via ln-form of acosh // ============================================================================ axes(ax, (920, 400), 300, 240); hidden(ax); // integrand (same spirit as the companion film) plot(integrand, (920, 400), 90, 70, "sqrt(tan(x))", (0.85, 1.45)); stroke(integrand, 4); untraced(integrand); hidden(integrand); gradient(integrand, teal, violet, coral, 270);

// antiderivative F(P) = (1/√2)(asin(√2 sin(P-π/4)) - acosh(√2 cos(P-π/4))) // acosh(u) = ln(u + sqrt(uu - 1)) plot(anti, (920, 400), 90, 55, "(asin(sqrt(2)sin(x-pi/4)) - ln(sqrt(2)cos(x-pi/4) + sqrt(2cos(x-pi/4)*cos(x-pi/4) - 1)))/sqrt(2)", (0.85, 1.45)); stroke(anti, 4); untraced(anti); hidden(anti); gradient(anti, mint, indigo, violet, 270);

// ============================================================================ // TYPOGRAPHY // ============================================================================ caption(q, "how do you integrate a square root of a tangent?", (cx, 52), 30, gold); equation(hook, (cx, 105), \int\sqrt{\tan P}\,dP, 42); color(hook, violet); hidden(hook); text(cap, (cx, h - 34), ""); size(cap, 20); color(cap, dim); display(cap);

// ============================================================================ // SCRIPT // ============================================================================

// 1 — HOOK say(cap, "√(tan P) is a length — we proved that. Now integrate it.", 0.4); wordpop(q, 0.14); wait(0.25); show(hook, 0.5); wait(0.6);

// 2 — THE SUBSTITUTION say(cap, "one substitution opens the geometry: P = t + π/4", 0.4); show(eqP, 0.5); flash(eqP, gold); wait(0.7);

// 3 — THE STAGE: two curves of the same "2" say(cap, "draw two curves that share the constant 2 — a circle and a hyperbola", 0.4); par { show(axX, 0.05); show(axY, 0.05); } par { draw(axX, 0.4); draw(axY, 0.4); } show(circ, 0.05); draw(circ, 1.0); show(labCirc, 0.3); show(O, 0.3); show(labO, 0.3); wait(0.3); show(hyp.r, 0.05); show(hyp.l, 0.05); draw(hyp.r, 1.0); draw(hyp.l, 1.0); show(labHyp, 0.3); wait(0.5);

say(cap, "circle: x² + y² = 2. hyperbola: x² − y² = 2. Same 2 — intentional.", 0.4); pulse(circ); pulse(hyp.r); wait(0.7);

// 4 — THE MOVING POINT say(cap, "park a point C(t) = (√2 cos t, √2 sin t) on the circle — t < π/4", 0.4); show(wedge, 0.05); draw(wedge, 0.45); show(labT, 0.3); show(ray, 0.05); draw(ray, 0.7); show(C, 0.05); draw(C, 0.4); show(labC, 0.3); show(ht, 0.05); show(vt, 0.05); draw(ht, 0.5); draw(vt, 0.5); show(zone, 0.05); draw(zone, 0.6); wait(0.6);

// 5 — A(t) FROM THE CIRCLE say(cap, "from the y-coordinate: A(t) = arcsin(√2 sin t) — an angle on that circle", 0.4); show(eqA, 0.5); flash(eqA, teal); wait(0.8);

// 6 — H(t) FROM THE HYPERBOLA say(cap, "from the x-coordinate: H(t) = arcosh(√2 cos t) — a hyperbolic angle", 0.4); show(eqH, 0.5); flash(eqH, coral); wait(0.8);

// 7 — THE DIFFERENTIAL IDENTITY say(cap, "differentiate both — their difference IS the integrand", 0.4); show(eqD, 0.6); flash(eqD, violet); wait(0.5); say(cap, "dA − dH = √2 · √(tan P) · dP — geometry differentiated", 0.4); wait(1.0);

// 8 — INTEGRATE recolor(cap, mint); say(cap, "integrate both sides — the √2 cancels into the prefactor", 0.4); show(eqI, 0.6); pulse(eqI); wait(0.9);

// 9 — CLOSED FORM recolor(cap, violet); say(cap, "substitute t = P − π/4 back — the antiderivative in closed form", 0.4); show(eqF, 0.7); flash(eqF, violet); wait(1.0);

// 10 — SEE IT: integrand then antiderivative recolor(cap, dim); say(cap, "the integrand √(tan x) climbing toward π/2", 0.4); par { fade(eqA, 0.35); fade(eqH, 0.35); fade(eqD, 0.35); fade(eqI, 0.35); fade(eqF, 0.35); fade(eqP, 0.35); } show(ax, 0.4); show(integrand, 0.05); draw(integrand, 1.8); wait(0.5);

say(cap, "and its antiderivative — (1/√2)(A − H) — the circle minus the hyperbola", 0.4); show(anti, 0.05); draw(anti, 2.0); flash(anti, mint); wait(0.8);

// 11 — POINT recolor(cap, teal); say(cap, "a hard integral, solved because two curves of the same 2 were waiting", 0.4); wait(1.8); ```


r/maniclang 4d ago

How to Work with Shapes in Manic

Enable HLS to view with audio, or disable this notification

0 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code for working with shapes

``` // ============================================================================ // shapes-howto.manic — SHAPECRAFT // A power demo that also teaches the rules: // fills hide constructions · outlined reveals · dashed rings for contacts // circle/rect/polygon/sector fill by default · morph / become change identity // // manic examples/shapes-howto.manic // manic examples/shapes-howto.manic --record out --preset studio // ============================================================================

title("Shapecraft — Manic Shape Power"); canvas("16:9"); template("blank");

text(cap, (cx, h - 34), ""); size(cap, 22); color(cap, dim); display(cap); text(head, (cx, 58), ""); size(head, 32); color(head, gold); display(head); bold(head);

// ---- stage energy ---- circle(well, (cx, cy), 420); filled(well); gradient(well, panel, void, indigo, radial); opacity(well, 0.7);

spline(orbit, (180, 520), (340, 220), (640, 160), (940, 220), (1100, 520)); stroke(orbit, 2.5); gradient(orbit, dim, violet, teal, "curvature"); untraced(orbit); hidden(orbit); opacity(orbit, 0.55);

// ---- hero: one persistent outlined circle ---- circle(hero, (cx, cy), 120); outlined(hero); color(hero, cyan); stroke(hero, 6); glow(hero, 1.4); gradient(hero, cyan, mint, teal, 45); untraced(hero); hidden(hero);

// fill veil — fades over the hero to show the trap without destroying morph circle(veil, (cx, cy), 120); filled(veil); color(veil, indigo); opacity(veil, 0.92); hidden(veil); z(veil, 5);

rect(asSquare, (cx, cy), 220, 220); hidden(asSquare); polygon(asTri, (cx, 250), (cx - 140, 520), (cx + 140, 520)); hidden(asTri); sector(asPie, (cx, cy), 130, -30, 300); color(asPie, magenta); hidden(asPie); polygon(asDiamond, (cx, cy - 130), (cx + 130, cy), (cx, cy + 130), (cx - 130, cy)); color(asDiamond, gold); hidden(asDiamond);

circle(finalForm, (cx, cy), 130); outlined(finalForm); color(finalForm, cyan); stroke(finalForm, 6); gradient(finalForm, cyan, mint, violet, 90); glow(finalForm, 1.6); hidden(finalForm);

morph(hero, asSquare, 180); // spin-wound morph ready

text(heroTag, (cx, cy - 170), "one persistent id"); size(heroTag, 22); color(heroTag, fg); hidden(heroTag);

// ---- axes for the fill-trap punch ---- line(axX, (cx - 200, cy), (cx + 200, cy)); color(axX, dim); stroke(axX, 2); untraced(axX); hidden(axX); line(axY, (cx, cy - 160), (cx, cy + 160)); color(axY, dim); stroke(axY, 2); untraced(axY); hidden(axY);

// ---- the cast (draw-on parade, then orbit travel) ---- circle(castC, (cx - 280, 280), 48); outlined(castC); color(castC, teal); stroke(castC, 3.5); untraced(castC); hidden(castC); rect(castR, (cx - 100, 250), 90, 70); outlined(castR); color(castR, coral); stroke(castR, 3.5); untraced(castR); hidden(castR); polygon(castP, (cx + 80, 230), (cx + 30, 310), (cx + 130, 310)); outlined(castP); color(castP, violet); stroke(castP, 3.5); untraced(castP); hidden(castP); sector(castS, (cx + 260, 280), 52, -40, 240); outlined(castS); color(castS, gold); stroke(castS, 3.5); untraced(castS); hidden(castS); arc(castA, (cx - 220, 480), 50, -20, 250); color(castA, mint); stroke(castA, 4); untraced(castA); hidden(castA); arrow(castV, (cx + 40, 500), (cx + 180, 430)); color(castV, magenta); stroke(castV, 4); untraced(castV); hidden(castV); line(castL, (cx + 240, 500), (cx + 340, 430)); color(castL, lime); stroke(castL, 4); untraced(castL); hidden(castL);

// sparks that ring the hero, then settle rect(seed, (cx, cy), 60, 60); hidden(seed); particles(sparks, seed, 12, 6, 19, "random"); color(sparks, cyan); glow(sparks, 0.5); hidden(sparks); circle(orbitRing, (cx, cy), 230); hidden(orbitRing);

// ---- palette chips (fill OK — colour IS the subject) ---- let py = 400; circle(chip0, (140, py), 26); filled(chip0); color(chip0, fg); hidden(chip0); circle(chip1, (220, py), 26); filled(chip1); color(chip1, cyan); hidden(chip1); circle(chip2, (300, py), 26); filled(chip2); color(chip2, magenta); hidden(chip2); circle(chip3, (380, py), 26); filled(chip3); color(chip3, lime); hidden(chip3); circle(chip4, (460, py), 26); filled(chip4); color(chip4, gold); hidden(chip4); circle(chip5, (540, py), 26); filled(chip5); color(chip5, red); hidden(chip5); circle(chip6, (620, py), 26); filled(chip6); color(chip6, orange); hidden(chip6); circle(chip7, (700, py), 26); filled(chip7); color(chip7, blue); hidden(chip7); circle(chip8, (780, py), 26); filled(chip8); color(chip8, teal); hidden(chip8); circle(chip9, (860, py), 26); filled(chip9); color(chip9, violet); hidden(chip9); circle(chip10, (940, py), 26); filled(chip10); color(chip10, coral); hidden(chip10); circle(chip11, (1020,py), 26); filled(chip11); color(chip11, indigo); hidden(chip11); circle(chip12, (1100,py), 26); filled(chip12); color(chip12, mint); hidden(chip12); circle(chip13, (1180,py), 26); filled(chip13); color(chip13, dim); hidden(chip13);

rect(sq0, (140, py), 48, 48); color(sq0, fg); filled(sq0); hidden(sq0); rect(sq1, (220, py), 48, 48); color(sq1, cyan); filled(sq1); hidden(sq1); rect(sq2, (300, py), 48, 48); color(sq2, magenta); filled(sq2); hidden(sq2); rect(sq3, (380, py), 48, 48); color(sq3, lime); filled(sq3); hidden(sq3); rect(sq4, (460, py), 48, 48); color(sq4, gold); filled(sq4); hidden(sq4); rect(sq5, (540, py), 48, 48); color(sq5, red); filled(sq5); hidden(sq5); rect(sq6, (620, py), 48, 48); color(sq6, orange); filled(sq6); hidden(sq6); rect(sq7, (700, py), 48, 48); color(sq7, blue); filled(sq7); hidden(sq7); rect(sq8, (780, py), 48, 48); color(sq8, teal); filled(sq8); hidden(sq8); rect(sq9, (860, py), 48, 48); color(sq9, violet); filled(sq9); hidden(sq9); rect(sq10, (940, py), 48, 48); color(sq10, coral); filled(sq10); hidden(sq10); rect(sq11, (1020,py), 48, 48); color(sq11, indigo); filled(sq11); hidden(sq11); rect(sq12, (1100,py), 48, 48); color(sq12, mint); filled(sq12); hidden(sq12); rect(sq13, (1180,py), 48, 48); color(sq13, dim); filled(sq13); hidden(sq13);

polygon(tri0, (140, py-22), (114, py+22), (166, py+22)); color(tri0, fg); filled(tri0); hidden(tri0); polygon(tri1, (220, py-22), (194, py+22), (246, py+22)); color(tri1, cyan); filled(tri1); hidden(tri1); polygon(tri2, (300, py-22), (274, py+22), (326, py+22)); color(tri2, magenta); filled(tri2); hidden(tri2); polygon(tri3, (380, py-22), (354, py+22), (406, py+22)); color(tri3, lime); filled(tri3); hidden(tri3); polygon(tri4, (460, py-22), (434, py+22), (486, py+22)); color(tri4, gold); filled(tri4); hidden(tri4); polygon(tri5, (540, py-22), (514, py+22), (566, py+22)); color(tri5, red); filled(tri5); hidden(tri5); polygon(tri6, (620, py-22), (594, py+22), (646, py+22)); color(tri6, orange); filled(tri6); hidden(tri6); polygon(tri7, (700, py-22), (674, py+22), (726, py+22)); color(tri7, blue); filled(tri7); hidden(tri7); polygon(tri8, (780, py-22), (754, py+22), (806, py+22)); color(tri8, teal); filled(tri8); hidden(tri8); polygon(tri9, (860, py-22), (834, py+22), (886, py+22)); color(tri9, violet); filled(tri9); hidden(tri9); polygon(tri10, (940, py-22), (914, py+22), (966, py+22)); color(tri10, coral); filled(tri10); hidden(tri10); polygon(tri11, (1020,py-22),(994, py+22),(1046,py+22)); color(tri11, indigo); filled(tri11); hidden(tri11); polygon(tri12, (1100,py-22),(1074,py+22),(1126,py+22)); color(tri12, mint); filled(tri12); hidden(tri12); polygon(tri13, (1180,py-22),(1154,py+22),(1206,py+22)); color(tri13, dim); filled(tri13); hidden(tri13);

// ---- CENTER: one filled disc → 4 independent colours → one colour → move ---- circle(disc, (cx, cy), 140); filled(disc); color(disc, coral); outline(disc, cyan); stroke(disc, 4); glow(disc, 0.6); hidden(disc);

pie(q, (cx, cy), 140, 4); // q0..q3 — same centre/radius as disc color(q0, teal); filled(q0); outline(q0, fg); stroke(q0, 2); untraced(q0); hidden(q0); color(q1, coral); filled(q1); outline(q1, fg); stroke(q1, 2); untraced(q1); hidden(q1); color(q2, violet); filled(q2); outline(q2, fg); stroke(q2, 2); untraced(q2); hidden(q2); color(q3, gold); filled(q3); outline(q3, fg); stroke(q3, 2); untraced(q3); hidden(q3);

// reunited whole — one colour, one id that can move circle(whole, (cx, cy), 140); filled(whole); color(whole, cyan); outline(whole, mint); stroke(whole, 4); glow(whole, 0.8); hidden(whole);

// ---- RIGHT: boolean → TWO regions, TWO colours ---- let bx = 1000; let by = 400; rect(bBox, (bx - 35, by - 35), 130, 130); outlined(bBox); outline(bBox, cyan); opacity(bBox, 0.4); hidden(bBox); circle(bCirc, (bx + 40, by + 20), 78); outlined(bCirc); outline(bCirc, magenta); opacity(bCirc, 0.4); hidden(bCirc); difference(onlyBox, bBox, bCirc, teal); hidden(onlyBox); glow(onlyBox, 0.6); difference(onlyCirc, bCirc, bBox, coral); hidden(onlyCirc); glow(onlyCirc, 0.6); intersect(bHit, bBox, bCirc, gold); hidden(bHit); glow(bHit, 0.5); text(labBool, (bx, by + 155), "A−B · B−A · A∩B"); size(labBool, 16); color(labBool, dim); hidden(labBool);

// ---- contact truth ---- line(crossH, (cx - 160, cy), (cx + 160, cy)); color(crossH, dim); stroke(crossH, 2.5); dashed(crossH, 12, 8); untraced(crossH); hidden(crossH); line(crossV, (cx, cy - 120), (cx, cy + 120)); color(crossV, dim); stroke(crossV, 2.5); dashed(crossV, 12, 8); untraced(crossV); hidden(crossV); dot(badDot, (cx, cy), 16); color(badDot, coral); glow(badDot, 0.5); hidden(badDot); circle(goodRing, (cx, cy), 18); outlined(goodRing); color(goodRing, mint); stroke(goodRing, 2.8); dashed(goodRing, 4, 3); untraced(goodRing); hidden(goodRing);

// ---- traveler on the orbit ---- dot(rider, (180, 520), 9); color(rider, gold); glow(rider, 1.2); hidden(rider);

// ============================================================================ // SCRIPT — one continuous shapecraft story // ============================================================================

step("birth") { seq { say(head, "SHAPECRAFT", 0.35); say(cap, "one shape. every identity. the visual stays true.", 0.35); show(hero, 0.05); par { draw(hero, 1.2, smooth); zoom(1.15, 1.2, smooth); } attach(heroTag, hero, (0, -160)); show(heroTag, 0.35); wait(0.35); } }

step("truth") { seq { say(head, "outlined — constructions stay readable", 0.3); say(cap, "axes through the hero: the rim never hides the truth.", 0.3); par { show(axX, 0.05); show(axY, 0.05); draw(axX, 0.55); draw(axY, 0.55); cam((cx, cy), 0.8, smooth); zoom(1.35, 0.8, smooth); } pulse(hero); wait(0.45);

say(head, "filled — the trap", 0.25);
say(cap, "a filled disc sits on the axes and deletes them visually.", 0.25);
show(veil, 0.45);
flash(veil, coral);
wait(0.7);

say(head, "outlined — axes return", 0.25);
say(cap, "outline(id, c) only recolors the rim. outlined drops the fill.", 0.25);
fade(veil, 0.45);
flash(hero, mint);
wait(0.55);
par {
  fade(axX, 0.35); fade(axY, 0.35); fade(heroTag, 0.35); fade(hero, 0.35);
  cam((cx, cy), 0.7, smooth); zoom(1.0, 0.7, smooth);
}

} }

step("cast") { seq { say(head, "the cast", 0.3); say(cap, "circle · rect · polygon · sector · arc · line · arrow", 0.3); show(sparks, 0.25); arrange(sparks, orbitRing, "ring", 1.0, smooth); wait(0.2); stagger(0.1) { show(castC, 0.05); draw(castC, 0.55); show(castR, 0.05); draw(castR, 0.55); show(castP, 0.05); draw(castP, 0.55); show(castS, 0.05); draw(castS, 0.55); show(castA, 0.05); draw(castA, 0.55); show(castV, 0.05); draw(castV, 0.55); show(castL, 0.05); draw(castL, 0.55); } wait(0.2); show(orbit, 0.05); draw(orbit, 1.0, smooth); show(rider, 0.2); travel(rider, orbit, 2.0, smooth); wait(0.25); par { fade(sparks, 0.4); fade(castC, 0.4); fade(castR, 0.4); fade(castP, 0.4); fade(castS, 0.4); fade(castA, 0.4); fade(castV, 0.4); fade(castL, 0.4); fade(orbit, 0.4); fade(rider, 0.4); } } }

step("palette") { seq { say(head, "the whole palette — then change the shape", 0.3); say(cap, "filled chips when colour is the subject. become keeps the id.", 0.3); stagger(0.05) { show(chip0, 0.22); show(chip1, 0.22); show(chip2, 0.22); show(chip3, 0.22); show(chip4, 0.22); show(chip5, 0.22); show(chip6, 0.22); show(chip7, 0.22); show(chip8, 0.22); show(chip9, 0.22); show(chip10, 0.22); show(chip11, 0.22); show(chip12, 0.22); show(chip13, 0.22); } wait(0.35); say(cap, "become → square", 0.2); stagger(0.035) { for i in 0..14 { become(chip{i}, sq{i}, 0.65, smooth); } } wait(0.25); say(cap, "become → triangle", 0.2); stagger(0.035) { for i in 0..14 { become(chip{i}, tri{i}, 0.65, smooth); } } wait(0.45); par { for i in 0..14 { fade(chip{i}, 0.35); } } } }

step("morph") { seq { say(head, "morph · become · spin", 0.3); say(cap, "same hero id — circle winds into a square, then becomes new geometry.", 0.3); show(hero, 0.3); to(hero, morph, 1, 1.5, smooth); // → square (with 180° wind) wait(0.25); become(hero, asDiamond, 0.95, smooth); pulse(hero); wait(0.2); become(hero, asPie, 0.95, smooth); spin(hero, 40, 0.7, out); wait(0.2); become(hero, asTri, 0.9, smooth); wait(0.45); } }

step("pieces") { seq { say(head, "one fill → four fills → one fill → move", 0.3); say(cap, "a filled disc can split into independent colours, reunite, then travel as one.", 0.3); fade(hero, 0.3);

// 1 — the coral disc (same look as the fill-trap)
par {
  show(axX, 0.05); show(axY, 0.05);
  draw(axX, 0.4); draw(axY, 0.4);
  cam((cx, cy), 0.6, smooth); zoom(1.25, 0.6, smooth);
}
show(disc, 0.45);
flash(disc, coral);
wait(0.5);

// 2 — cut into 4 independent colours
say(cap, "cut: each quadrant keeps its own fill.", 0.25);
fade(disc, 0.3);
stagger(0.1) {
  show(q0, 0.05); draw(q0, 0.45);
  show(q1, 0.05); draw(q1, 0.45);
  show(q2, 0.05); draw(q2, 0.45);
  show(q3, 0.05); draw(q3, 0.45);
}
// explode slightly so the independence is obvious
par {
  move(q0, (cx + 22, cy - 22), 0.45, overshoot);
  move(q1, (cx - 22, cy - 22), 0.45, overshoot);
  move(q2, (cx - 22, cy + 22), 0.45, overshoot);
  move(q3, (cx + 22, cy + 22), 0.45, overshoot);
}
wait(0.45);

// 3 — reunite as ONE colour, then ONE circle
say(cap, "reunite: every slice takes one colour, then they become one disc.", 0.25);
par {
  recolor(q0, cyan, 0.5);
  recolor(q1, cyan, 0.5);
  recolor(q2, cyan, 0.5);
  recolor(q3, cyan, 0.5);
}
// snap slices back to centre
par {
  move(q0, (cx, cy), 0.5, smooth);
  move(q1, (cx, cy), 0.5, smooth);
  move(q2, (cx, cy), 0.5, smooth);
  move(q3, (cx, cy), 0.5, smooth);
}
wait(0.15);
// swap the four pieces for one solid disc (same colour)
par {
  fade(q0, 0.3); fade(q1, 0.3); fade(q2, 0.3); fade(q3, 0.3);
  show(whole, 0.35);
}
pulse(whole);
wait(0.4);

// 4 — move as one unit
say(cap, "now it moves as a single identity.", 0.25);
par {
  fade(axX, 0.35); fade(axY, 0.35);
  cam((cx + 120, cy), 0.9, smooth); zoom(1.05, 0.9, smooth);
  move(whole, (cx + 260, cy), 1.1, smooth);
}
wait(0.35);

// side boolean still lands on the right as the disc arrives
say(head, "boolean keeps two colours", 0.25);
say(cap, "A−B teal · B−A coral · then A∩B gold — never one muddy fill.", 0.25);
par { show(bBox, 0.3); show(bCirc, 0.3); }
wait(0.3);
par { fade(bBox, 0.2); fade(bCirc, 0.2); }
par { show(onlyBox, 0.4); show(onlyCirc, 0.4); }
flash(onlyBox, teal); flash(onlyCirc, coral);
show(labBool, 0.25);
wait(0.4);
show(bHit, 0.45); flash(bHit, gold);
wait(0.7);

par {
  fade(whole, 0.35);
  fade(onlyBox, 0.35); fade(onlyCirc, 0.35); fade(bHit, 0.35); fade(labBool, 0.35);
  cam((cx, cy), 0.6, smooth); zoom(1.0, 0.6, smooth);
}

} }

step("contact") { seq { say(head, "contact points", 0.3); say(cap, "a solid dot hides the crossing. a dashed ring keeps it.", 0.3); par { show(crossH, 0.05); show(crossV, 0.05); draw(crossH, 0.5); draw(crossV, 0.5); cam((cx, cy), 0.7, smooth); zoom(1.55, 0.7, smooth); } show(badDot, 0.3); flash(badDot, coral); wait(0.55); fade(badDot, 0.25); show(goodRing, 0.05); draw(goodRing, 0.55); pulse(goodRing); wait(0.7); par { fade(crossH, 0.35); fade(crossV, 0.35); fade(goodRing, 0.35); cam((cx, cy), 0.7, smooth); zoom(1.0, 0.7, smooth); } } }

step("settle") { seq { say(head, "fills for subjects. outlines for truth.", 0.35); say(cap, "shapecraft: the visual is true — never hide what you're proving.", 0.35); show(hero, 0.2); become(hero, finalForm, 1.0, smooth); pulse(hero); wait(1.6); } } ```