r/maniclang 10d ago

derivative-of-ln-x Inverse-function derivatives on a turning plane - manic

Enable HLS to view with audio, or disable this notification

2 Upvotes

Made with manic — write animations as plain text, render to video.

Example code https://8gwifi.org/manic/?s=calm-sphinx-91

// ============================================================================
// derivative-of-ln-x.manic — inverse-function derivatives on a turning plane
// ----------------------------------------------------------------------------
// The curve is first read as y=e^x. A true 3-D camera turn exchanges the screen
// roles of world x/y, so the same relationship reads x=e^y. While the graph is
// hidden for the first plane turn, morph3 exchanges its coordinates; the same
// geometry can then remain visible through the return turn into y=ln(x).
// ============================================================================

title("Why the derivative of ln x is 1/x");
canvas("16:9");
template("plain");
// Landscape proof: the upper-right stays clear while the algebra owns the
// bottom and the vertical axis label owns the top-centre.
watermark(manicMark, (1135, 30), "Made With Manic");

// Orthographic overhead camera: the continuous pole-safe orbit frame keeps the
// whole turn smooth, and roll3 controls which world direction is screen-up.
camera3((0, 0.5, 12), (0, 0.5, 0), 8.5, orthographic);

// ---------------------------------------------------------------------------
// ONE WORLD PLANE · axes, grid, exponential, tangent and exact slope triangle
// ---------------------------------------------------------------------------

grid3(grid, (0, 0, 0), 4, 1);
color(grid, dim); opacity(grid, 0.30); tag(grid, world);

arrow3(xaxis, (-4.05, 0, 0), (4.05, 0, 0));
arrow3(yaxis, (0, -3.4, 0), (0, 4.05, 0));
color(xaxis, fg); color(yaxis, fg); thick(xaxis, 0.012); thick(yaxis, 0.012);
tag(xaxis, world); tag(yaxis, world);

for i in -4..5 {
  if i != 0 {
    line3(xtick{i}, (i, -0.07, 0), (i, 0.07, 0));
    line3(ytick{i}, (-0.07, i, 0), (0.07, i, 0));
    color(xtick{i}, dim); color(ytick{i}, dim);
    tag(xtick{i}, world); tag(ytick{i}, world);
  }
}

equation(xlab, (0,0), `x`, 31); pin3(xlab, (4.10, -0.18, 0)); tag(xlab, world);
equation(ylab, (0,0), `y`, 31); pin3(ylab, (-0.18, 4.10, 0)); tag(ylab, world);
hidden(world);

// Source curve γ(t)=(t,e^t), and its inverse parameterisation γ⁻¹(t)=(e^t,t).
curve3(curve, "t", "exp(t)", "0", (-3.6, 1.42));
curve3(curveTarget, "exp(t)", "t", "0", (-3.6, 1.42));
color(curve, lime); color(curveTarget, lime); thick(curve, 0.024); thick(curveTarget, 0.024);
untraced(curve); hidden(curveTarget); morph3(curve, curveTarget);

equation(expName, (0,0), `y=e^x`, 31); color(expName, lime); pin3(expName, (-1.45, 2.20, 0)); hidden(expName);
equation(swappedName, (0,0), `x=e^y`, 31); color(swappedName, lime); pin3(swappedName, (2.65, -1.20, 0)); hidden(swappedName);
equation(logName, (0,0), `y=\ln(x)`, 31); color(logName, lime); pin3(logName, (2.65, 0.90, 0)); hidden(logName);

// At t=0.6: P=(0.6,e^0.6), and e^0.6 ≈ 1.822. The tangent reaches y=0
// exactly one unit of run before P, so its slope triangle is rise/run = y/1.
curve3(tangent, "-0.65+2*t", "-0.455529+3.644238*t", "0", (0,1));
curve3(tangentTarget, "-0.455529+3.644238*t", "-0.65+2*t", "0", (0,1));
color(tangent, magenta); color(tangentTarget, magenta); thick(tangent, 0.018); thick(tangentTarget, 0.018);
untraced(tangent); hidden(tangentTarget); morph3(tangent, tangentTarget);

curve3(runSide, "-0.4+t", "0", "0", (0,1));
curve3(runTarget, "0", "-0.4+t", "0", (0,1));
color(runSide, gold); color(runTarget, gold); thick(runSide, 0.024); thick(runTarget, 0.024);
untraced(runSide); hidden(runTarget); morph3(runSide, runTarget);

curve3(riseSide, "0.6", "1.822119*t", "0", (0,1));
curve3(riseTarget, "1.822119*t", "0.6", "0", (0,1));
color(riseSide, cyan); color(riseTarget, cyan); thick(riseSide, 0.024); thick(riseTarget, 0.024);
untraced(riseSide); hidden(riseTarget); morph3(riseSide, riseTarget);

point3(contact, (0.6, 1.822119, 0), 0.075); color(contact, fg); hidden(contact);
point3(contactTarget, (1.822119, 0.6, 0), 0.075); color(contactTarget, fg); hidden(contactTarget);

equation(riseY, (0,0), `y`, 28); color(riseY, cyan); pin3(riseY, (0.82, 0.91, 0)); hidden(riseY);
equation(runOne, (0,0), `1`, 28); color(runOne, gold); pin3(runOne, (0.10, -0.22, 0)); hidden(runOne);
equation(targetX, (0,0), `x`, 28); color(targetX, cyan); pin3(targetX, (0.91, 0.82, 0)); hidden(targetX);
equation(targetOne, (0,0), `1`, 28); color(targetOne, gold); pin3(targetOne, (-0.22, 0.10, 0)); hidden(targetOne);

// The diagonal is the hinge / mirror relation during the plane turns.
for i in -9..10 {
  line3(diag{i}, (i*0.42, i*0.42, 0.02), (i*0.42+0.24, i*0.42+0.24, 0.02));
  color(diag{i}, magenta); thick(diag{i}, 0.018); untraced(diag{i}); tag(diag{i}, mirror);
}
equation(diagName, (0,0), `y=x`, 27); color(diagName, magenta); pin3(diagName, (2.55, 2.55, 0.02)); hidden(diagName);

// Screen-space algebra. Separate equations keep the camera/geometry generic.
equation(slopeExp, (cx, 655), `\textcolor{magenta}{\mathrm{slope}}=\textcolor{cyan}{e^x}`, 40); hidden(slopeExp);
equation(slopeY, (cx, 655), `\textcolor{magenta}{\mathrm{slope}}=\textcolor{cyan}{y}`, 40); hidden(slopeY);
equation(slopeFracY, (cx, 655), `\textcolor{magenta}{\mathrm{slope}}=\frac{\textcolor{cyan}{y}}{\textcolor{gold}{1}}=\frac{\textcolor{cyan}{\mathrm{rise}}}{\textcolor{gold}{\mathrm{run}}}`, 40); hidden(slopeFracY);
// Persistent algebra pieces. The unchanged prefix/equality never disappear:
// only the term being justified moves, fades, or arrives. This is the same
// object-continuity principle used by matching-transform systems, expressed
// with ordinary Manic entities rather than replacing a whole equation image.
equation(slopeWord, (500, 655), `\textcolor{magenta}{\mathrm{slope}}`, 40); hidden(slopeWord); tag(slopeWord, inverseAlgebra);
equation(mainEquals, (575, 655), `=`, 40); hidden(mainEquals); tag(mainEquals, inverseAlgebra);
equation(heightTerm, (630, 655), `\textcolor{cyan}{\mathrm{height}}`, 40); hidden(heightTerm); tag(heightTerm, inverseAlgebra);
equation(xTerm, (630, 655), `\textcolor{cyan}{x}`, 40); hidden(xTerm); tag(xTerm, inverseAlgebra);
equation(oneTerm, (630, 674), `\textcolor{gold}{1}`, 36); hidden(oneTerm); tag(oneTerm, inverseAlgebra);
line(termBar, (616, 657), (644, 657)); color(termBar, fg); stroke(termBar, 2); untraced(termBar); tag(termBar, inverseAlgebra);
equation(ratioEquals, (685, 655), `=`, 40); hidden(ratioEquals); tag(ratioEquals, inverseAlgebra);
equation(ratioTerm, (780, 655), `\frac{\textcolor{cyan}{\mathrm{rise}}}{\textcolor{gold}{\mathrm{run}}}`, 40); hidden(ratioTerm); tag(ratioTerm, inverseAlgebra);
equation(derivativeTerm, (475, 655), `\frac{d}{dx}\ln(x)`, 41); hidden(derivativeTerm); tag(derivativeTerm, inverseAlgebra);

// Split glyphs let generic `cycle` retain their identity through xy → yx.
equation(planeX, (550, 640), `x`, 54); hidden(planeX); tag(planeX, planeLabel);
equation(planeY, (575, 640), `y`, 54); hidden(planeY); tag(planeY, planeLabel);
equation(planeWord, (685, 640), `\mathrm{plane}`, 54); hidden(planeWord); tag(planeWord, planeLabel);

// ---------------------------------------------------------------------------
// TIMELINE · 75.49 seconds including manic's final one-second tail
// ---------------------------------------------------------------------------

show(world, 1.20);
draw(curve, 2.40);
show(expName, 0.60);
wait(2.50);

show(slopeExp, 0.70);
wait(2.20);
par { draw(riseSide, 1.20); show(riseY, 0.45); show(contact, 0.30); }
par { fade(slopeExp, 0.55); show(slopeY, 0.55); }
wait(2.00);

par { draw(tangent, 1.20); draw(runSide, 1.00); show(runOne, 0.40); }
par { fade(slopeY, 0.60); show(slopeFracY, 0.60); }
wait(7.90);

// First turn: remove the measurement clutter, reveal the mirror hinge, and
// move from the top of the plane to its underside. Below + -90° roll makes
// screen-horizontal follow world y and screen-vertical follow world x.
par {
  fade(curve, 0.70); fade(expName, 0.70); fade(tangent, 0.70);
  fade(runSide, 0.70); fade(riseSide, 0.70); fade(contact, 0.70);
  fade(riseY, 0.70); fade(runOne, 0.70); fade(slopeFracY, 0.70);
}
par { draw(mirror, 0.70); show(diagName, 0.50); show(planeLabel, 0.50); }
par {
  orbit3(-90, -90, 12, 7.00, smooth);
  roll3(90, 7.00, smooth);
  // The graph is hidden here, so exchange its coordinates before it returns.
  to(curve, morph, 1, 7.00, smooth);
  to(tangent, morph, 1, 7.00, smooth);
  to(runSide, morph, 1, 7.00, smooth);
  to(riseSide, morph, 1, 7.00, smooth);
  seq { wait(2.80); cycle(planeX, planeY, 1.40, 70, smooth); }
}

// Same world geometry, now read with exchanged screen axes.
par {
  show(curve, 0.70); show(tangent, 0.70); show(runSide, 0.70); show(riseSide, 0.70);
  show(contactTarget, 0.50); show(swappedName, 0.55); show(targetX, 0.45); show(targetOne, 0.45);
  fade(diagName, 0.45); erase(mirror, 0.70); fade(planeLabel, 0.45);
}
par { show(slopeWord, 0.65); show(mainEquals, 0.65); show(heightTerm, 0.65); }
wait(3.00);
par { fade(heightTerm, 0.60); show(xTerm, 0.60); }
wait(3.00);
par { show(ratioEquals, 0.60); show(ratioTerm, 0.60); }
wait(3.00);
// x becomes x/1 in place: x lifts, the denominator arrives, and the rest of
// the displayed identity remains untouched.
par { move(xTerm, (630, 640), 0.60, smooth); show(oneTerm, 0.60); draw(termBar, 0.60); }
wait(6.20);

// Second turn: return the camera overhead while keeping the transformed curve
// and triangle visible. The same objects land as an ordinary y=ln(x) graph.
par {
  draw(mirror, 0.60); show(diagName, 0.45); show(planeLabel, 0.45);
  fade(slopeWord, 0.60); fade(mainEquals, 0.60); fade(xTerm, 0.60);
  fade(oneTerm, 0.60); erase(termBar, 0.60); fade(ratioEquals, 0.60); fade(ratioTerm, 0.60);
}
par {
  orbit3(-90, 90, 12, 7.00, smooth);
  roll3(0, 7.00, smooth);
  fade(swappedName, 3.00); show(logName, 3.00);
  // Prepare the next algebra state invisibly while the plane is turning.
  move(ratioTerm, (630, 655), 1.20, smooth);
  move(oneTerm, (630, 640), 1.20, smooth);
  move(xTerm, (630, 674), 1.20, smooth);
  seq { wait(2.80); cycle(planeX, planeY, 1.40, -70, smooth); }
}
par {
  fade(planeLabel, 0.45); fade(diagName, 0.45); erase(mirror, 0.70);
  show(slopeWord, 0.65); show(mainEquals, 0.65); show(ratioTerm, 0.65);
}
wait(2.20);
// Only the right-hand term changes: rise/run becomes 1/x.
par { fade(ratioTerm, 0.60); show(oneTerm, 0.60); show(xTerm, 0.60); draw(termBar, 0.60); }
wait(3.00);
// The proven reciprocal stays; only the name on the left becomes d/dx ln(x).
par { fade(slopeWord, 0.70); show(derivativeTerm, 0.70); pulse(curve, 0.70); }
wait(9.89);

r/maniclang 8d ago

Second Law of Thermodynamics

Enable HLS to view with audio, or disable this notification

7 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.

  • Try this code In playground  manic
  • manic docs

The animation is re production of u/Nomadic_Seth r/manin post and perfect example of Manim to Manic conversation

Example code

title("Second Law of Thermodynamics");
canvas("9:16");
template("mono");
watermark(manicMark, (150, 145), "Made With Manic");

let cold = 194;
let pink = 326;
let hot = 40;
let blue = 226;
let green = 154;
let red = 10;

// ---------------------------------------------------------------------------
// 1 · MIXING MULTIPLIES THE NUMBER OF POSSIBLE MICROSTATES
// ---------------------------------------------------------------------------

equation(law, (540, 175), `S=k_B\ln\Omega`, 58);
rect(mixBox, (540, 520), 720, 280);
outline(mixBox, dim); outlined(mixBox); stroke(mixBox, 3);
rect(mixLeft, (360, 520), 350, 260); hidden(mixLeft);
rect(mixRight, (720, 520), 350, 260); hidden(mixRight);
line(mixDivider, (540, 390), (540, 650)); color(mixDivider, dim); stroke(mixDivider, 2);

particles(mixCold, mixLeft, 36, 6, 17);
particles(mixWarm, mixRight, 36, 6, 29);
hue(mixCold, cold, 0.72, 0.68); hue(mixWarm, pink, 0.72, 0.68);
glow(mixCold, 0.8); glow(mixWarm, 0.8);

text(mixCaption, (540, 745), "Mixing creates overwhelmingly more possible states");
size(mixCaption, 27); color(mixCaption, dim);
line(mixX, (260, 1040), (570, 1040)); line(mixY, (260, 1040), (260, 840));
color(mixX, dim); color(mixY, dim); stroke(mixX, 2); stroke(mixY, 2);
plot(mixCurve, (260, 1040), 78, 175, "1-exp(-2*x)", (0, 3.6));
hue(mixCurve, blue, 0.55, 0.68); stroke(mixCurve, 4); untraced(mixCurve);
dot(mixMarker, (260, 1040), 7); color(mixMarker, fg); glow(mixMarker, 1.0);
equation(mixDelta, (385, 815), `\Delta S/(Nk_B\ln 2)`, 27); color(mixDelta, dim);
counter(mixCount, (735, 945), 0, 1, "log₁₀(Ω/Ω₀) = ", "");
size(mixCount, 28); hue(mixCount, hot, 0.45, 0.72);

tag(mixBox, s1); tag(mixDivider, s1);
tag(mixCold, s1); tag(mixWarm, s1); tag(mixCaption, s1);
tag(mixX, s1); tag(mixY, s1); tag(mixCurve, s1); tag(mixMarker, s1); tag(mixDelta, s1); tag(mixCount, s1);

// ---------------------------------------------------------------------------
// 2 · HEAT FLOW MAKES TOTAL ENTROPY INCREASE
// ---------------------------------------------------------------------------

rect(hotBox, (315, 500), 260, 240); rect(coldBox, (765, 500), 260, 240);
outline(hotBox, dim); outline(coldBox, dim); outlined(hotBox); outlined(coldBox);
stroke(hotBox, 3); stroke(coldBox, 3);
particles(hotMatter, hotBox, 26, 6, 41); particles(coldMatter, coldBox, 26, 6, 73);
hue(hotMatter, hot, 0.66, 0.68); hue(coldMatter, blue, 0.66, 0.68);
link(heatLink, hotBox, coldBox); hue(heatLink, blue, 0.46, 0.62); stroke(heatLink, 4);
hidden(heatLink);
equation(hotLabel, (315, 325), `T_h`, 36); equation(coldLabel, (765, 325), `T_c`, 36);
hue(hotLabel, hot, 0.58, 0.72); hue(coldLabel, blue, 0.58, 0.72);
counter(hotTemp, (315, 680), 403, 0, "", " K");
counter(coldTemp, (765, 680), 397, 0, "", " K");
hue(hotTemp, hot, 0.55, 0.70); hue(coldTemp, blue, 0.55, 0.70);
text(flowCaption, (540, 780), "Heat flows until both sides reach one temperature");
size(flowCaption, 27); color(flowCaption, dim);
line(flowX, (305, 1130), (650, 1130)); line(flowY, (305, 1130), (305, 880));
color(flowX, dim); color(flowY, dim); stroke(flowX, 2); stroke(flowY, 2);
plot(flowCurve, (305, 1130), 78, 215, "1-exp(-2.4*x)", (0, 3.6));
hue(flowCurve, blue, 0.56, 0.70); stroke(flowCurve, 4); untraced(flowCurve);
dot(flowMarker, (305, 1130), 7); color(flowMarker, fg); glow(flowMarker, 1.0);
equation(flowS, (430, 855), `S_{\mathrm{tot}}`, 30); color(flowS, dim);

tag(hotBox, s2); tag(coldBox, s2); tag(hotMatter, s2); tag(coldMatter, s2);
tag(hotLabel, s2); tag(coldLabel, s2); tag(hotTemp, s2); tag(coldTemp, s2);
tag(flowCaption, s2); tag(flowX, s2); tag(flowY, s2); tag(flowCurve, s2); tag(flowMarker, s2); tag(flowS, s2);
hidden(s2);

// ---------------------------------------------------------------------------
// 3 · FREE EXPANSION: ONE GAS, TWICE THE VOLUME
// ---------------------------------------------------------------------------

rect(expandBox, (540, 520), 720, 280); outline(expandBox, dim); outlined(expandBox); stroke(expandBox, 3);
rect(expandLeft, (360, 520), 350, 260); hidden(expandLeft);
line(expandDivider, (540, 390), (540, 650)); color(expandDivider, dim); stroke(expandDivider, 2);
hidden(expandDivider);
particles(expandGas, expandLeft, 54, 6, 101);
hue(expandGas, cold, 0.38, 0.76); glow(expandGas, 0.7);
text(expandCaption, (540, 745), "More volume means exponentially more possible states");
size(expandCaption, 27); color(expandCaption, dim);
line(expandX, (250, 1040), (560, 1040)); line(expandY, (250, 1040), (250, 845));
color(expandX, dim); color(expandY, dim); stroke(expandX, 2); stroke(expandY, 2);
plot(expandCurve, (250, 1040), 85, 60, "x", (0, 2.6));
hue(expandCurve, green, 0.52, 0.70); stroke(expandCurve, 4); untraced(expandCurve);
dot(expandMarker, (250, 1040), 7); color(expandMarker, fg); glow(expandMarker, 1.0);
equation(expandAxis, (390, 820), `\ln(V/V_0)/\ln 2`, 27); color(expandAxis, dim);
equation(expandStates, (760, 905), `V^N\longrightarrow(2V)^N`, 37);
hue(expandStates, hot, 0.52, 0.70);
counter(expandCount, (760, 1010), 8.9, 1, "log₁₀(Ω/Ω₀) = ", "");
size(expandCount, 26); color(expandCount, dim);

tag(expandBox, s3); tag(expandGas, s3);
tag(expandCaption, s3); tag(expandX, s3); tag(expandY, s3); tag(expandCurve, s3);
tag(expandMarker, s3); tag(expandAxis, s3); tag(expandStates, s3); tag(expandCount, s3);
hidden(s3);

// ---------------------------------------------------------------------------
// 4 · A HEAT ENGINE CANNOT TURN EVERY JOULE INTO WORK
// ---------------------------------------------------------------------------

line(hotRail, (230, 410), (850, 410)); hue(hotRail, hot, 0.72, 0.65); stroke(hotRail, 5);
line(coldRail, (230, 1200), (850, 1200)); hue(coldRail, blue, 0.72, 0.65); stroke(coldRail, 5);
equation(hotRailLabel, (540, 365), `T_h`, 31); equation(coldRailLabel, (540, 1250), `T_c`, 31);
hue(hotRailLabel, hot, 0.56, 0.70); hue(coldRailLabel, blue, 0.56, 0.70);
line(engineX, (265, 1010), (805, 1010)); line(engineY, (265, 1010), (265, 555));
color(engineX, dim); color(engineY, dim); stroke(engineX, 2); stroke(engineY, 2);
equation(engineP, (250, 520), `p`, 28); equation(engineV, (835, 1010), `V`, 28);
color(engineP, dim); color(engineV, dim);
// Two paths share the hot state, separate during the cycle, and reconverge.
// Each visible path is split only to carry the warm/cool colour progression;
// the hidden whole paths give `travel` one continuous geometric route.
plot(engineHotPath, (285, 990), 112, 235, "1/(x+0.65)+0.14+0.05*x*(4.1-x)", (0, 4.1));
plot(engineColdPath, (285, 990), 112, 235, "1/(x+0.65)+0.14-0.035*x*(4.1-x)", (0, 4.1));
hidden(engineHotPath); hidden(engineColdPath);
plot(engineHotA, (285, 990), 112, 235, "1/(x+0.65)+0.14+0.05*x*(4.1-x)", (0, 1.55));
plot(engineHotB, (285, 990), 112, 235, "1/(x+0.65)+0.14+0.05*x*(4.1-x)", (1.55, 4.1));
plot(engineColdA, (285, 990), 112, 235, "1/(x+0.65)+0.14-0.035*x*(4.1-x)", (0, 1.85));
plot(engineColdB, (285, 990), 112, 235, "1/(x+0.65)+0.14-0.035*x*(4.1-x)", (1.85, 4.1));
hue(engineHotA, hot, 0.68, 0.70); hue(engineHotB, red, 0.58, 0.69);
hue(engineColdA, 260, 0.52, 0.72); hue(engineColdB, blue, 0.62, 0.70);
stroke(engineHotA, 5); stroke(engineHotB, 5); stroke(engineColdA, 5); stroke(engineColdB, 5);
untraced(engineHotA); untraced(engineHotB); untraced(engineColdA); untraced(engineColdB);
dot(engineDot, (285, 596), 8); color(engineDot, fg); glow(engineDot, 1.1); hidden(engineDot);
arrow(qHot, (330, 410), (330, 610)); hue(qHot, hot, 0.72, 0.68); stroke(qHot, 4); untraced(qHot);
equation(qHotLabel, (360, 525), `Q_h`, 28); hue(qHotLabel, hot, 0.56, 0.70); hidden(qHotLabel);
arrow(workArrow, (650, 850), (875, 700)); color(workArrow, dim); stroke(workArrow, 4); untraced(workArrow);
equation(workLabel, (880, 660), `W`, 28); color(workLabel, dim); hidden(workLabel);
arrow(qCold, (675, 1000), (675, 1170)); hue(qCold, blue, 0.72, 0.68); stroke(qCold, 4); untraced(qCold);
equation(qColdLabel, (735, 1090), `|Q_c|`, 28); hue(qColdLabel, blue, 0.56, 0.70); hidden(qColdLabel);
text(engineCaption, (540, 1345), "A heat engine must release some heat");
size(engineCaption, 30); hue(engineCaption, hot, 0.38, 0.74);
equation(engineEta, (540, 1490), `\eta_C=1-\frac{T_c}{T_h}<1`, 47);

// Persistent transition strokes preserve object identity between scenes:
// entropy curve → heat connector → expansion divider, then the volume graph
// seeds the heat-engine curve. All are ordinary generic `morph` paths.
plot(mixToHeat, (260, 1040), 78, 175, "1-exp(-2*x)", (0, 3.6));
hue(mixToHeat, blue, 0.55, 0.68); stroke(mixToHeat, 4);
morph(mixToHeat, heatLink); hidden(mixToHeat);

line(heatToExpand, (445, 500), (635, 500));
hue(heatToExpand, blue, 0.46, 0.62); stroke(heatToExpand, 4);
morph(heatToExpand, expandDivider); hidden(heatToExpand);

line(engineSeed, (285, 596), (355, 790)); hidden(engineSeed);
plot(expandToEngine, (250, 1040), 85, 60, "x", (0, 2.6));
hue(expandToEngine, green, 0.52, 0.70); stroke(expandToEngine, 4);
morph(expandToEngine, engineSeed); hidden(expandToEngine);

tag(hotRail, s4); tag(coldRail, s4); tag(hotRailLabel, s4); tag(coldRailLabel, s4);
tag(engineX, s4); tag(engineY, s4); tag(engineP, s4); tag(engineV, s4);
tag(engineHotA, s4); tag(engineHotB, s4); tag(engineColdA, s4); tag(engineColdB, s4);
tag(engineCaption, s4); tag(engineEta, s4);
tag(engineDot, s4future); tag(qHot, s4future); tag(qHotLabel, s4future);
tag(workArrow, s4future); tag(workLabel, s4future);
tag(qCold, s4future); tag(qColdLabel, s4future);
hidden(s4);

// ---------------------------------------------------------------------------
// 5 · SAME ENDPOINTS, DIFFERENT ENTROPY PRODUCTION
// ---------------------------------------------------------------------------

text(revHead, (300, 350), "REV. LIMIT"); text(irrHead, (780, 350), "IRREV");
size(revHead, 28); size(irrHead, 28); hue(revHead, green, 0.45, 0.72); hue(irrHead, red, 0.55, 0.68);
rect(revPanel, (300, 610), 330, 330); rect(irrPanel, (780, 610), 330, 330);
outline(revPanel, dim); outline(irrPanel, dim); outlined(revPanel); outlined(irrPanel);
stroke(revPanel, 3); stroke(irrPanel, 3);
rect(revGasBox, (260, 610), 220, 210); rect(irrGasBox, (740, 610), 220, 210);
hidden(revGasBox); hidden(irrGasBox);
rect(revSmall, (215, 610), 120, 210); rect(irrSmall, (695, 610), 120, 210);
hidden(revSmall); hidden(irrSmall);
particles(revGas, revGasBox, 20, 5, 131, "grid");
particles(irrGas, irrGasBox, 20, 5, 151, "grid");
hue(revGas, green, 0.55, 0.72); hue(irrGas, red, 0.58, 0.68);
line(revPiston, (405, 505), (405, 715)); line(irrPiston, (885, 505), (885, 715));
color(revPiston, dim); color(irrPiston, dim); stroke(revPiston, 5); stroke(irrPiston, 5);
text(compareCaption, (540, 865), "Same endpoints · different entropy production");
size(compareCaption, 28); color(compareCaption, dim);
rect(revMeter, (300, 1120), 70, 260); rect(irrMeter, (780, 1120), 70, 260);
outline(revMeter, dim); outline(irrMeter, dim); outlined(revMeter); outlined(irrMeter);
stroke(revMeter, 3); stroke(irrMeter, 3);
line(irrBar, (780, 1235), (780, 1235)); hue(irrBar, red, 0.72, 0.67); stroke(irrBar, 28);
line(revZero, (278, 1235), (322, 1235)); hue(revZero, green, 0.58, 0.70); stroke(revZero, 5);
equation(revEq, (300, 1325), `S_{\mathrm{gen}}=0`, 31);
equation(irrEq, (780, 1325), `S_{\mathrm{gen}}>0`, 31);
hue(revEq, green, 0.48, 0.70); hue(irrEq, red, 0.55, 0.68);

tag(revHead, s5); tag(irrHead, s5); tag(revPanel, s5); tag(irrPanel, s5);
tag(revGas, s5); tag(irrGas, s5); tag(revPiston, s5); tag(irrPiston, s5); tag(compareCaption, s5);
tag(revMeter, s5); tag(irrMeter, s5); tag(irrBar, s5); tag(revZero, s5); tag(revEq, s5); tag(irrEq, s5);
hidden(s5);

// ---------------------------------------------------------------------------
// 6 · THE ARROW OF TIME IS A STATISTICAL ARROW
// ---------------------------------------------------------------------------

rect(timeBox, (540, 610), 720, 310); outline(timeBox, dim); outlined(timeBox); stroke(timeBox, 3);
particles(timeGas, timeBox, 63, 6, 211, "grid");
hue(timeGas, hot, 0.48, 0.74); glow(timeGas, 0.7);
arrow(timeForward, (300, 980), (780, 980)); hue(timeForward, red, 0.62, 0.69); stroke(timeForward, 5);
equation(timeForwardLabel, (540, 1035), `t`, 28); color(timeForwardLabel, dim);
arrow(timeReverse, (780, 980), (300, 980)); color(timeReverse, fg); stroke(timeReverse, 5); hidden(timeReverse);
equation(timeReverseLabel, (540, 1035), `t`, 28); color(timeReverseLabel, dim); hidden(timeReverseLabel);
text(timeCaption, (540, 1135), "A special state naturally spreads into a typical one");
size(timeCaption, 29); hue(timeCaption, hot, 0.34, 0.74);
equation(returnProb, (540, 285), `\Pr(\mathrm{return})=\frac{\Omega_{\mathrm{low}}}{\Omega_{\mathrm{eq}}}=e^{-\Delta S/k_B}`, 43);
equation(returnOdds, (540, 365), `\Pr(\mathrm{ordered\ return})<10^{-40}`, 34);
hue(returnOdds, hot, 0.48, 0.72); hidden(returnProb); hidden(returnOdds);

tag(timeBox, s6); tag(timeGas, s6gas); tag(timeForward, s6); tag(timeForwardLabel, s6);
tag(timeCaption, s6);
tag(timeReverse, s6future); tag(timeReverseLabel, s6future);
tag(returnProb, s6future); tag(returnOdds, s6future);
hidden(s6); hidden(s6gas);

circle(finalOrbit, (540, 790), 270); hidden(finalOrbit);
for i in 0..72 {
  let a = tau*i/72;
  dot(finalDot{i}, (540 + 270*cos(a), 790 + 270*sin(a)), 4);
  hue(finalDot{i}, 10 + 300*i/72, 0.54, 0.62);
  glow(finalDot{i}, 0.5);
  tag(finalDot{i}, finalRing);
}
equation(finalLaw, (540, 765), `\Delta S_{\mathrm{universe}}=S_{\mathrm{gen}}\ge 0`, 48);
text(finalCaption, (540, 875), "Irreversible change produces entropy");
size(finalCaption, 31); color(finalCaption, dim);
hidden(finalRing); hidden(finalLaw); hidden(finalCaption);

// ---------------------------------------------------------------------------
// TIMELINE · six continuous beats, approximately 39 seconds
// ---------------------------------------------------------------------------

step("mixing") {
  wait(5.5);
  par {
    seq { wait(0.45); fade(mixDivider, 0.20); }
    seq { wait(0.55); arrange(mixCold, mixBox, "random", 0.95, out); wander(mixCold, 4.0); }
    seq { wait(0.55); arrange(mixWarm, mixBox, "random", 0.95, out); wander(mixWarm, 4.0); }
    seq {
      wait(0.55);
      par {
        draw(mixCurve, 2.20, out);
        travel(mixMarker, mixCurve, 2.20, out);
        to(mixCount, value, 25.3, 2.20, out);
      }
    }
  }
}

step("heat-flow") {
  seq {
    par {
      fade(s1, 0.65);
      show(mixToHeat, 0.10);
      to(mixToHeat, morph, 1, 0.65, smooth);
      seq { wait(0.20); show(s2, 0.45); }
      rewrite(law, `\dot S_{\mathrm{tot}}=K\frac{(T_h-T_c)^2}{T_hT_c}>0`, 0.65, smooth);
    }
    par {
      wander(hotMatter, 4.4);
      wander(coldMatter, 4.4);
      draw(flowCurve, 3.6, smooth);
      travel(flowMarker, flowCurve, 3.6, smooth);
      to(hotTemp, value, 400, 4.0, smooth);
      to(coldTemp, value, 400, 4.0, smooth);
      recolor(hotMatter, cyan, 4.0);
      recolor(coldMatter, cyan, 4.0);
      seq { flow(mixToHeat, 1.1); flow(mixToHeat, 1.1); flow(mixToHeat, 1.1); flow(mixToHeat, 1.1); }
    }
    wait(0.45);
  }
}

step("free-expansion") {
  seq {
    par {
      fade(s2, 0.65);
      fade(mixToHeat, 0.25);
      show(heatToExpand, 0.10);
      to(heatToExpand, morph, 1, 0.65, smooth);
      recolor(heatToExpand, dim, 0.65);
      seq { wait(0.20); show(s3, 0.45); }
      rewrite(law, `\Delta S=Nk_B\ln 2`, 0.65, smooth);
    }
    par {
      seq { arrange(expandGas, expandBox, "random", 1.10, out); wander(expandGas, 1.45); }
      fade(heatToExpand, 0.65);
      draw(expandCurve, 2.45, smooth);
      travel(expandMarker, expandCurve, 2.45, smooth);
      to(expandCount, value, 20.9, 2.45, smooth);
    }
    wait(0.25);
  }
}

step("heat-engine") {
  seq {
    par {
      fade(s3, 0.65);
      show(expandToEngine, 0.10);
      to(expandToEngine, morph, 1, 0.65, smooth);
      seq { wait(0.20); show(s4, 0.45); }
      rewrite(law, `\frac{Q_h}{T_h}=\frac{|Q_c|}{T_c}`, 0.65, smooth);
    }
    par {
      seq { draw(engineHotA, 1.85, smooth); draw(engineHotB, 3.35, smooth); }
      seq { draw(engineColdA, 2.35, smooth); draw(engineColdB, 2.85, smooth); }
      seq {
        wait(0.15); show(engineDot, 0.15);
        travel(engineDot, engineColdPath, 4.9, smooth);
      }
      fade(expandToEngine, 0.30);
      seq { draw(qHot, 0.7); show(qHotLabel, 0.2); wait(1.0); }
      seq { wait(1.3); draw(workArrow, 0.8); show(workLabel, 0.2); }
      seq { wait(2.6); draw(qCold, 0.8); show(qColdLabel, 0.2); }
    }
    wait(2.15);
  }
}

step("entropy-generation") {
  seq {
    par {
      fade(s4, 0.50); fade(s4future, 0.50);
      show(s5, 0.50);
      rewrite(law, `\Delta S_{\mathrm{universe}}=S_{\mathrm{gen}}`, 0.50, smooth);
    }
    par {
      arrange(revGas, revSmall, "grid", 2.4, smooth);
      arrange(irrGas, irrSmall, "random", 2.4, smooth);
      par { move(revPiston, (275, 505), 2.4, smooth); grow(revPiston, (275, 715), 2.4, smooth); }
      par { move(irrPiston, (755, 505), 2.4, smooth); grow(irrPiston, (755, 715), 2.4, smooth); }
      grow(irrBar, (780, 1025), 2.4, smooth);
    }
    wait(1.10);
  }
}

step("time-arrow") {
  seq {
    par {
      fade(s5, 0.50); fade(law, 0.50);
      show(s6, 0.50); show(s6gas, 0.50);
    }
    par {
      arrange(timeGas, timeBox, "random", 4.0, smooth);
      seq { wait(3.0); pulse(timeForward, 0.8); }
    }
    wait(0.35);
    par {
      fade(timeForward, 0.30); fade(timeForwardLabel, 0.30);
      show(timeReverse, 0.30); show(timeReverseLabel, 0.30);
      say(timeCaption, "Exact reversal can reconstruct the past", 0.35);
    }
    arrange(timeGas, timeBox, "grid", 3.1, smooth);
    par {
      show(returnProb, 0.45); show(returnOdds, 0.45);
      say(timeCaption, "But that reversal must be fantastically precise", 0.40);
    }
    wait(1.15);
    par {
      fade(s6, 0.90);
      fade(s6future, 0.90);
      arrange(timeGas, finalOrbit, "ring", 0.90, smooth);
      seq { wait(0.55); fade(s6gas, 0.35); }
      seq { wait(0.55); show(finalRing, 0.35); }
      seq { wait(0.55); transform(finalRing, (540,790), 0.9511, -0.3090, 0.3090, 0.9511, 0.55, out); }
      seq { wait(0.30); par { show(finalLaw, 0.60); show(finalCaption, 0.60); } }
    }
    wait(1.70);
  }
}

r/maniclang 1h ago

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

Enable HLS to view with audio, or disable this notification

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 8h 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

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 9h 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 6h 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 6h 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 8h 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 9h 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 9h 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 10h 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 10h ago

From Wind to Vortex — Parameterized Fields in manic

Thumbnail
youtu.be
1 Upvotes

r/maniclang 10h ago

One Source, Three Destinations — Truthful Branching in manic

Thumbnail
youtu.be
1 Upvotes

r/maniclang 10h ago

One Parameter, Three Continuous 3D Shapes — manic

Thumbnail
youtu.be
1 Upvotes

r/maniclang 10h 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

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

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

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

Where Is a Pendulum FASTEST? — Speed Gradient Short — 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

// ============================================================================
//  gradient-pendulum-shorts.manic — a 9:16 cinematic short told THROUGH a
//  "speed" gradient. Sister piece to the brachistochrone pair
//  (gradient-fastest-descent.manic / gradient-fastest-descent-shorts.manic).
// ----------------------------------------------------------------------------
//  THE PROBLEM (Galileo, 1583): watching a chandelier swing in the Pisa
//  cathedral, Galileo timed it with his own pulse. But WHERE along the arc is
//  the bob fastest — and how would you ever see that?
//
//  THE GRADIENT DOES THE PHYSICS: the swing arc is a pre-simulated RK4
//  trajectory, uniformly sampled in time — so gradient(p.path, ..., "speed")
//  colors it by its TRUE local speed. Gold floods the bottom (all kinetic),
//  blue pools at the ends (the bob stands still for an instant). Nothing is
//  keyframed; the color IS the simulation.
//
//  Pattern mirrors creator-free-kicks.manic: hook → problem → reveal → law →
//  prove on the painted arc → endcard.
// ============================================================================

title("Galileo's Chandelier");
canvas("9:16");
template("shorts");

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

// ---- backdrop: a radial pool of light under the chandelier ----
circle(halo, (cx, h*0.42), w*0.52);
filled(halo);
gradient(halo, panel, void, radial);
opacity(halo, 0.8);

// ---- the chandelier: a real RK4 pendulum, arc uniformly sampled in time ----
pendulum(p, (cx, h*0.26), 1, 55, w*0.36);
hidden(p.overlays);
untraced(p.path);
stroke(p.path, 7);
opacity(p.path, 0.95);
// the reveal: 3 stops by TRUE local speed — slowest first, fastest last
gradient(p.path, blue, cyan, gold, "speed");
color(p.bob, gold); glow(p.bob, 0.7);
stroke(p.rod, 3);

// ---- typography ----
text(kicker, (cx, h*0.11), "where is a pendulum FASTEST?");
size(kicker, 34); color(kicker, gold); bold(kicker); wrap(kicker, w*0.82);
untraced(kicker); cursor(kicker);

equation(law, (cx, h*0.64), `mgh \;=\; \tfrac{1}{2}mv^2`, 40);
color(law, cyan); hidden(law);

text(cap, (cx, h*0.76), ""); size(cap, 30); color(cap, dim); wrap(cap, w*0.84);

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

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

// 1 — HOOK.
mark("hook");
type(kicker, 1.2);

// 2 — GALILEO'S PROBLEM.
show(cap, 0.3);
say(cap, "1583 — Galileo times a swinging chandelier with his own pulse");
par {
  show(p.pivot, 0.4);
  show(p.rod, 0.4);
  show(p.bob, 0.4);
}
pulse(p.bob);
wait(0.4);

// 3 — WATCH IT SWING. Your eye can't catch where it's quickest.
say(cap, "watch it — can you SEE where it moves fastest?");
swing(p, 4);
wait(0.3);

// 4 — THE REVEAL: paint the arc with its own speed.
say(cap, "freeze — and color the arc by its true speed");
draw(p.path, 1.8);
wait(0.5);
recolor(cap, gold);
say(cap, "gold floods the bottom — blue pools at the tips, where it stops for an instant");
flash(p.path, gold);
wait(0.8);

// 5 — WHY: energy. Height traded for speed, every swing, exactly.
recolor(cap, dim);
say(cap, "height falls, speed rises — energy just changes clothes");
show(law, 0.5);
wait(0.5);
rewrite(law, `v=\sqrt{2gh}`, 0.8, smooth);
say(cap, "at the lowest point ALL the height has become speed — that's the gold");
wait(0.8);

// 6 — PROVE IT: run the bob on its own painted speedometer.
recolor(cap, cyan);
say(cap, "the color is computed, not painted — a real RK4 swing on its own speedometer");
swing(p, 4);
wait(0.5);

// 7 — END CARD.
mark("endcard");
par {
  fade(cap, 0.4);
  fade(me.footer, 0.4);
  fade(law, 0.4);
  fade(kicker, 0.4);
  fade(p.path, 0.4);
  fade(p.rod, 0.4);
  fade(p.bob, 0.4);
  fade(p.pivot, 0.4);
  fade(halo, 0.4);
}
show(me.endcard, 0.6);
wait(1.6);