Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Calculus & functions

Each block is the whole file — copy it into x.manic and run manic x.manic (live) or --record out (video).

calculus-demo

The flagship: two big ideas on one curve. A tangent slides along a bell curve with a live slope readout (flat at the peak), then the area sweeps open while the integral climbs to its true value — on properly numbered, scaled axes.

title("Calculus, visualized — the derivative and the integral");
canvas("16:9");

// screen anchor for the plot: math (0,0) maps here. Placing it at cx-450 with
// sx=150 centres the domain 0..6 under the canvas centre.
let ox = cx - 450;
let oy = cy + 120;
let sx = 150;          // screen px per unit of x
let sy = 170;          // screen px per unit of y

// ---------------- header ----------------
text(hdr, (cx, 54), "Two big ideas, one curve");
size(hdr, 32); color(hdr, cyan); bold(hdr); display(hdr); hidden(hdr);

text(flabel, (cx, 94), "f(x) = a smooth bell-shaped curve");
size(flabel, 18); color(flabel, dim); display(flabel); hidden(flabel);

// ---------------- the curve ----------------
axes(ax, (ox, oy), 930, 205, 1);
color(ax, dim); untraced(ax);

// axis labels, at the arrow tips
text(xlab, (ox + 958, oy + 6), "x");
size(xlab, 24); color(xlab, dim); display(xlab); hidden(xlab);

text(ylab, (ox, oy - 232), "f(x)");
size(ylab, 24); color(ylab, dim); display(ylab); hidden(ylab);

// numbered scale — each label sits at the plot's real coordinate:
// x = k  ->  (ox + k*sx, oy)      y = v  ->  (ox, oy - v*sy)
line(tx1, (ox + 1*sx, oy - 7), (ox + 1*sx, oy + 7)); color(tx1, dim); tag(tx1, scale); hidden(tx1);
line(tx2, (ox + 2*sx, oy - 7), (ox + 2*sx, oy + 7)); color(tx2, dim); tag(tx2, scale); hidden(tx2);
line(tx3, (ox + 3*sx, oy - 7), (ox + 3*sx, oy + 7)); color(tx3, dim); tag(tx3, scale); hidden(tx3);
line(tx4, (ox + 4*sx, oy - 7), (ox + 4*sx, oy + 7)); color(tx4, dim); tag(tx4, scale); hidden(tx4);
line(tx5, (ox + 5*sx, oy - 7), (ox + 5*sx, oy + 7)); color(tx5, dim); tag(tx5, scale); hidden(tx5);
line(tx6, (ox + 6*sx, oy - 7), (ox + 6*sx, oy + 7)); color(tx6, dim); tag(tx6, scale); hidden(tx6);

text(nx1, (ox + 1*sx, oy + 28), "1"); size(nx1, 16); color(nx1, dim); tag(nx1, scale); hidden(nx1);
text(nx2, (ox + 2*sx, oy + 28), "2"); size(nx2, 16); color(nx2, dim); tag(nx2, scale); hidden(nx2);
text(nx3, (ox + 3*sx, oy + 28), "3"); size(nx3, 16); color(nx3, dim); tag(nx3, scale); hidden(nx3);
text(nx4, (ox + 4*sx, oy + 28), "4"); size(nx4, 16); color(nx4, dim); tag(nx4, scale); hidden(nx4);
text(nx5, (ox + 5*sx, oy + 28), "5"); size(nx5, 16); color(nx5, dim); tag(nx5, scale); hidden(nx5);
text(nx6, (ox + 6*sx, oy + 28), "6"); size(nx6, 16); color(nx6, dim); tag(nx6, scale); hidden(nx6);

line(tyh, (ox - 7, oy - 0.5*sy), (ox + 7, oy - 0.5*sy)); color(tyh, dim); tag(tyh, scale); hidden(tyh);
line(ty1, (ox - 7, oy - 1*sy),   (ox + 7, oy - 1*sy));   color(ty1, dim); tag(ty1, scale); hidden(ty1);
text(nyh, (ox - 36, oy - 0.5*sy), "0.5"); size(nyh, 16); color(nyh, dim); tag(nyh, scale); hidden(nyh);
text(ny1, (ox - 24, oy - 1*sy),   "1");   size(ny1, 16); color(ny1, dim); tag(ny1, scale); hidden(ny1);

plot(f, (ox, oy), sx, sy, "exp(-0.5*(x-3)*(x-3))", (0, 6));
color(f, cyan); stroke(f, 4); untraced(f);

// ---------------- captions (swap per act) ----------------
text(capd, (cx, h - 46), "THE DERIVATIVE   —   the slope at every point");
size(capd, 22); color(capd, gold); bold(capd); display(capd); hidden(capd);

text(capi, (cx, h - 46), "THE INTEGRAL   —   the area underneath");
size(capi, 22); color(capi, lime); bold(capi); display(capi); hidden(capi);

text(endcap, (cx, h - 46), "two views of one curve");
size(endcap, 22); color(endcap, cyan); bold(endcap); display(endcap); hidden(endcap);

// ---------------- derivative pieces ----------------
tangent(tan, f, 0.4, 220);
color(tan, gold); hidden(tan);

slope(sl, f, 0.4);
color(sl, gold); hidden(sl);

// ---------------- integral pieces ----------------
area(reg, f, 0, 0);
color(reg, lime);

text(ivlabel, (cx + 330, cy - 150), "area so far");
size(ivlabel, 16); color(ivlabel, dim); display(ivlabel); hidden(ivlabel);

integral(iv, f, 0, 0, (cx + 330, cy - 110));
size(iv, 30); color(iv, lime); hidden(iv);

// ================= timeline =================
show(hdr, 0.6);
show(flabel, 0.4);
draw(ax, 0.8);
par {
  show(xlab, 0.4);
  show(ylab, 0.4);
  show(scale, 0.4);
}
draw(f, 1.6);
wait(0.4);

// --- Act 1: the derivative ---
show(capd, 0.5);
show(tan, 0.5);
show(sl, 0.4);
wait(0.3);
par {                                 // climb to the peak — slope goes to zero
  to(tan, x, 3, 2.2);
  to(sl, x, 3, 2.2);
}
pulse(sl, 0.5);
wait(0.3);
par {                                 // over the top — slope turns negative
  to(tan, x, 5.6, 2.6);
  to(sl, x, 5.6, 2.6);
}
wait(0.4);

// --- Act 2: the integral ---
par {
  fade(capd, 0.4);
  fade(tan, 0.4);
  fade(sl, 0.4);
}
show(capi, 0.5);
show(ivlabel, 0.3);
show(iv, 0.3);
par {                                 // sweep the area open, number climbs
  to(reg, x, 6, 4);
  to(iv, x, 6, 4);
}
wait(0.4);
flash(iv, lime);
pulse(iv, 0.5);
wait(0.4);

// --- close ---
fade(capi, 0.4);
show(endcap, 0.6);

reactive-integral

The reactive-math gold example: integration by parts stays one continuous equation, then a plot verifies that the antiderivative’s slope is the integrand and +C is a family of vertical translations. Uses rewrite, not hand-positioned formula pieces.

// ============================================================================
// reactive-integral.manic — one calculation, one continuous visual world
// ----------------------------------------------------------------------------
// Integration by parts uses one structured equation. `rewrite` keeps unchanged
// RaTeX parts alive while only the mathematical difference moves or enters.
// The graph then verifies the result: the slope of F is the height of f, and
// +C becomes a visible family of vertical translations.
// ============================================================================

title("A Reactive Integral");
canvas("16:9");
template("plain");
watermark(manicMark, (1135, 30), "Made With Manic");

// Shared colour roles follow the same mathematical objects through every act.
// x / u is magenta; cos, sin and their antiderivative relation are cyan.
text(kicker, (285, 38), "ONE INTEGRAL · ONE CONTINUOUS STORY");
size(kicker, 18); color(kicker, dim); bold(kicker); hidden(kicker);

text(headline, (430, 72), "Watch only the mathematics that changes");
size(headline, 28); color(headline, fg); bold(headline); hidden(headline);

// ---------------------------------------------------------------------------
// ONE PERSISTENT EQUATION
// ---------------------------------------------------------------------------

equation(
  work,
  (cx, 145),
  `\int \textcolor{magenta}{x}\,\textcolor{cyan}{\cos(x)}\,dx`,
  46
);
hidden(work);

// The by-parts roles remain visible while the expression changes above them.
equation(uChoice, (330, 225), `u=x`, 28); color(uChoice, magenta); hidden(uChoice);
equation(dvChoice, (525, 225), `dv=\cos(x)\,dx`, 28); color(dvChoice, cyan); hidden(dvChoice);
equation(duChoice, (760, 225), `du=dx`, 28); color(duChoice, magenta); hidden(duChoice);
equation(vChoice, (950, 225), `v=\sin(x)`, 28); color(vChoice, cyan); hidden(vChoice);

text(method, (cx, 278), "INTEGRATION BY PARTS");
size(method, 16); color(method, dim); bold(method); hidden(method);

// ---------------------------------------------------------------------------
// ONE GRAPH, ASKED THREE CONNECTED QUESTIONS
// ---------------------------------------------------------------------------

let ox = cx;
let oy = 515;
let sx = 108;
let sy = 28;

axes(ax, (ox, oy), 550, 165, 1);
color(ax, dim); opacity(ax, 0.50); untraced(ax);

// Integrand and one representative antiderivative (C=0).
plot(f, (ox, oy), sx, sy, "x*cos(x)", (-5, 5));
color(f, cyan); stroke(f, 5); glow(f, 0.8); untraced(f);

plot(bigF, (ox, oy), sx, sy, "x*sin(x)+cos(x)", (-5, 5));
color(bigF, lime); stroke(bigF, 5); dashed(bigF, 18, 11);
glow(bigF, 0.8); untraced(bigF);

// Numerical derivative of F: drawing it lands directly on f.
deriv(dF, bigF);
color(dF, gold); stroke(dF, 3); glow(dF, 0.5); untraced(dF);

// A moving tangent makes “F' = f” a local statement at every x.
tangent(tan, bigF, -3.8, 180); color(tan, gold); hidden(tan);
slope(slopeValue, bigF, -3.8, (18, -24)); color(slopeValue, gold); hidden(slopeValue);

equation(fLabel, (250, 338), `f(x)=x\cos(x)`, 28); color(fLabel, cyan); hidden(fLabel);
equation(FLabel, (1010, 338), `F(x)=x\sin(x)+\cos(x)`, 28); color(FLabel, lime); hidden(FLabel);
equation(proofLabel, (cx, 280), `F'(x)=f(x)=x\cos(x)`, 34); color(proofLabel, gold); hidden(proofLabel);

// +C does not change the derivative: it translates the same curve vertically.
plot(Fup, (ox, oy), sx, sy, "x*sin(x)+cos(x)+1.5", (-5, 5));
plot(Fdown, (ox, oy), sx, sy, "x*sin(x)+cos(x)-1.5", (-5, 5));
color(Fup, magenta); color(Fdown, magenta);
stroke(Fup, 3); stroke(Fdown, 3); opacity(Fup, 0.55); opacity(Fdown, 0.55);
dashed(Fup, 14, 9); dashed(Fdown, 14, 9);
untraced(Fup); untraced(Fdown);

equation(familyLabel, (cx, 280), `F(x)+C\quad\text{changes height, not slope}`, 32);
color(familyLabel, magenta); hidden(familyLabel);

text(caption, (cx, 690), "");
size(caption, 19); color(caption, dim); bold(caption);

// Axes and curves are `untraced`, so their draw progress starts at zero without
// also zeroing opacity. That lets `draw(...)` reveal their geometry later.

// ---------------------------------------------------------------------------
// TIMELINE
// ---------------------------------------------------------------------------

show(kicker, 0.45);
show(headline, 0.55);
say(caption, "Start with the question. Keep the stage — change only its state.");
show(work, 0.65);
wait(1.60);

// Choose u and dv by reusing the same semantic colours.
show(method, 0.35);
say(caption, "Choose the two roles. Colour preserves their identity.");
par { show(uChoice, 0.50); show(dvChoice, 0.65); }
wait(1.20);
par { show(duChoice, 0.50); show(vChoice, 0.50); }
wait(1.50);

// Build x sin(x) - integral sin(x) dx. Reusable glyphs travel to their new
// jobs while the obsolete cosine is the only original factor that leaves.
say(caption, "Apply integration by parts: persistent pieces move; new pieces arrive.");
rewrite(
  work,
  `\textcolor{magenta}{x}\textcolor{cyan}{\sin(x)}-\int\textcolor{cyan}{\sin(x)}\,dx`,
  1.05,
  smooth
);
wait(2.00);

// Only the remaining integral changes. x sin(x) stays untouched.
say(caption, "Resolve the remaining integral. The stable product never blinks.");
rewrite(
  work,
  `\textcolor{magenta}{x}\textcolor{cyan}{\sin(x)}+\textcolor{cyan}{\cos(x)}+\textcolor{gold}{C}`,
  1.00,
  smooth
);
wait(2.00);

// Move attention from symbolic manipulation into the same idea as geometry.
say(caption, "Now verify the answer visually — without leaving the scene.");
par {
  fade(method, 0.40);
  fade(uChoice, 0.40); fade(dvChoice, 0.40);
  fade(duChoice, 0.40); fade(vChoice, 0.40);
  draw(ax, 0.85);
}
draw(f, 1.80);
show(fLabel, 0.45);
wait(1.00);

draw(bigF, 1.80);
show(FLabel, 0.45);
wait(1.00);

// The tangent and its numeric slope glide continuously across F.
say(caption, "At every x, the slope of the green curve equals the blue height.");
par { show(tan, 0.45); show(slopeValue, 0.45); }
par {
  to(tan, x, 3.8, 4.80, smooth);
  to(slopeValue, x, 3.8, 4.80, smooth);
}
wait(0.60);

// The derivative trace lands directly over the original integrand.
show(proofLabel, 0.50);
draw(dF, 2.20);
flash(f, cyan);
wait(1.40);

// Finish with the geometric meaning of the integration constant.
say(caption, "+C creates a family of answers — same shape, same slope.");
par {
  fade(tan, 0.45); fade(slopeValue, 0.45); fade(dF, 0.45);
  fade(proofLabel, 0.45); fade(fLabel, 0.45); fade(FLabel, 0.45);
  show(familyLabel, 0.55);
  pulse(work, 0.70);
}
par { draw(Fup, 1.50); draw(Fdown, 1.50); }
wait(2.80);

rewrite-integration

A full manual-integration walkthrough, animated: one equation object is rewrite-morphed through all 21 steps — every substitution, partial-fraction split and back-substitution, each captioned with its rule — down to the closed form. Display-quality LaTeX throughout.

title("Rewrite Integration Steps");
canvas("16:9");
template("mono");

text(head, (cx, 58), "Integrating by rewriting the denominator");
display(head);
bold(head);
size(head, 32);
hidden(head);

text(sub, (cx, 94), "Manual integration — partial fractions by substitution");
size(sub, 20);
color(sub, dim);
hidden(sub);

rect(panelBox, (cx, cy + 14), w - 150, 380);
outline(panelBox, dim);
opacity(panelBox, 0.35);
hidden(panelBox);

text(stepTitle, (cx, 150), "");
display(stepTitle);
bold(stepTitle);
size(stepTitle, 26);
color(stepTitle, cyan);

text(ruleTag, (cx, 182), "");
size(ruleTag, 18);
color(ruleTag, dim);

text(watermark, (w - 96, h - 12), "Made with Manic");
size(watermark, 15);
color(watermark, dim);
hidden(watermark);

equation(work, (cx, cy + 20), `\int \frac{1}{(x - 1)(x + 2)}\,dx`, 34);
hidden(work);

equation(finalLine, (cx, h - 64), `\boxed{\,\frac{\log{\left(x - 1 \right)}}{3} - \frac{\log{\left(x + 2 \right)}}{3} + C\,}`, 34);
color(finalLine, lime);
hidden(finalLine);

show(head, 0.45);
show(sub, 0.45);
show(panelBox, 0.45);
show(watermark, 0.45);
show(work, 0.5);
wait(0.4);

par {
say(stepTitle, "1. u-Substitution", 0.25);
say(ruleTag, "Rule: URule", 0.25);
rewrite(work, `Let\ u = x - 1,\quad \frac{du}{dx} = 1`, 0.7, smooth);
}
wait(0.3);

par {
say(stepTitle, "2. u-Substitution", 0.25);
say(ruleTag, "Rule: URule", 0.25);
rewrite(work, `Let\ u = u + \frac{3}{2},\quad \frac{du}{d_u} = 1`, 0.7, smooth);
}
wait(0.3);

par {
say(stepTitle, "3. Rewrite", 0.25);
say(ruleTag, "Rule: RewriteRule", 0.25);
rewrite(work, `\int \frac{1}{3 u + \left(u - \frac{3}{2}\right)^{2} - \frac{9}{2}} \,d_u = \int - \frac{2}{3 \left(2 u + 3\right)} + \frac{2}{3 \left(2 u - 3\right)} \,d_u`, 0.7, smooth);
}
wait(0.3);

par {
say(stepTitle, "4. Sum rule", 0.25);
say(ruleTag, "Rule: AddRule", 0.25);
rewrite(work, `\int (f+g)\,d_u = \int f\,d_u + \int g\,d_u`, 0.7, smooth);
}
wait(0.3);

par {
say(stepTitle, "5. Constant factor", 0.25);
say(ruleTag, "Rule: ConstantTimesRule", 0.25);
rewrite(work, `\int - \frac{2}{3 \left(2 u + 3\right)} \,d_u = - \frac{2}{3} \int \frac{1}{2 u + 3} \,d_u`, 0.7, smooth);
}
wait(0.3);

par {
say(stepTitle, "6. u-Substitution", 0.25);
say(ruleTag, "Rule: URule", 0.25);
rewrite(work, `Let\ u = 2 u + 3,\quad \frac{du}{d_u} = 2`, 0.7, smooth);
}
wait(0.3);

par {
say(stepTitle, "7. Constant factor", 0.25);
say(ruleTag, "Rule: ConstantTimesRule", 0.25);
rewrite(work, `\int \frac{1}{2 u} \,d_u = \frac{1}{2} \int \frac{1}{u} \,d_u`, 0.7, smooth);
}
wait(0.3);

par {
say(stepTitle, "8. Reciprocal", 0.25);
say(ruleTag, "Rule: ReciprocalRule", 0.25);
rewrite(work, `\int \frac{1}{u} \,d_u = \ln|u| + C = \log{\left(u \right)}`, 0.7, smooth);
}
wait(0.3);

par {
say(stepTitle, "9. Simplify", 0.25);
say(ruleTag, "Rule: ConstantTimesRule", 0.25);
rewrite(work, `= \frac{\log{\left(u \right)}}{2} + C`, 0.7, smooth);
}
wait(0.3);

par {
say(stepTitle, "10. Back substitute", 0.25);
say(ruleTag, "Rule: URule", 0.25);
rewrite(work, `= \frac{\log{\left(2 u + 3 \right)}}{2} + C`, 0.7, smooth);
}
wait(0.3);

par {
say(stepTitle, "11. Simplify", 0.25);
say(ruleTag, "Rule: ConstantTimesRule", 0.25);
rewrite(work, `= - \frac{\log{\left(2 u + 3 \right)}}{3} + C`, 0.7, smooth);
}
wait(0.3);

par {
say(stepTitle, "12. Constant factor", 0.25);
say(ruleTag, "Rule: ConstantTimesRule", 0.25);
rewrite(work, `\int \frac{2}{3 \left(2 u - 3\right)} \,d_u = \frac{2}{3} \int \frac{1}{2 u - 3} \,d_u`, 0.7, smooth);
}
wait(0.3);

par {
say(stepTitle, "13. u-Substitution", 0.25);
say(ruleTag, "Rule: URule", 0.25);
rewrite(work, `Let\ u = 2 u - 3,\quad \frac{du}{d_u} = 2`, 0.7, smooth);
}
wait(0.3);

par {
say(stepTitle, "14. Constant factor", 0.25);
say(ruleTag, "Rule: ConstantTimesRule", 0.25);
rewrite(work, `\int \frac{1}{2 u} \,d_u = \frac{1}{2} \int \frac{1}{u} \,d_u`, 0.7, smooth);
}
wait(0.3);

par {
say(stepTitle, "15. Reciprocal", 0.25);
say(ruleTag, "Rule: ReciprocalRule", 0.25);
rewrite(work, `\int \frac{1}{u} \,d_u = \ln|u| + C = \log{\left(u \right)}`, 0.7, smooth);
}
wait(0.3);

par {
say(stepTitle, "16. Simplify", 0.25);
say(ruleTag, "Rule: ConstantTimesRule", 0.25);
rewrite(work, `= \frac{\log{\left(u \right)}}{2} + C`, 0.7, smooth);
}
wait(0.3);

par {
say(stepTitle, "17. Back substitute", 0.25);
say(ruleTag, "Rule: URule", 0.25);
rewrite(work, `= \frac{\log{\left(2 u - 3 \right)}}{2} + C`, 0.7, smooth);
}
wait(0.3);

par {
say(stepTitle, "18. Simplify", 0.25);
say(ruleTag, "Rule: ConstantTimesRule", 0.25);
rewrite(work, `= \frac{\log{\left(2 u - 3 \right)}}{3} + C`, 0.7, smooth);
}
wait(0.3);

par {
say(stepTitle, "19. Combine", 0.25);
say(ruleTag, "Rule: AddRule", 0.25);
rewrite(work, `= \frac{\log{\left(2 u - 3 \right)}}{3} - \frac{\log{\left(2 u + 3 \right)}}{3} + C`, 0.7, smooth);
}
wait(0.3);

par {
say(stepTitle, "20. Back substitute", 0.25);
say(ruleTag, "Rule: URule", 0.25);
rewrite(work, `= \frac{\log{\left(2 u \right)}}{3} - \frac{\log{\left(2 u + 6 \right)}}{3} + C`, 0.7, smooth);
}
wait(0.3);

par {
say(stepTitle, "21. Back substitute", 0.25);
say(ruleTag, "Rule: URule", 0.25);
rewrite(work, `= \frac{\log{\left(2 x - 2 \right)}}{3} - \frac{\log{\left(2 x + 4 \right)}}{3} + C`, 0.7, smooth);
}
wait(0.3);

show(finalLine, 0.8);
pulse(finalLine, 0.8);
wait(1.0);

reactive-world

The foundation for creator-reactive stories: five named step blocks keep one curve, equation, tangent, live slope, derivative plot and caption synchronized. Each step is also exported as a marker for seeking and future multi-format publishing.

// ============================================================================
// reactive-world.manic — one world, named state transitions
// ----------------------------------------------------------------------------
// Each `step` changes several existing representations together. The equation,
// tangent, live slope, derivative curve, caption and camera share one continuous
// stage; anything a step does not mention simply persists.
// ============================================================================

title("Reactive World — The Derivative Lives on the Curve");
canvas("9:16");
template("terminal");
watermark(manicMark, (865, 165), "Made With Manic");

creator(me, "@anish2good name=Calculus_World tagline=One_idea_many_views yt=zarigatongy x=@anish2good web=8gwifi.org/manic accent=cyan secondary=magenta footer=social cta=See_the_change safe=reels");
socials(me);

text(kicker, (540, 130), "ONE WORLD · FIVE NAMED STEPS");
size(kicker, 23); color(kicker, dim); bold(kicker); hidden(kicker);

text(headline, (540, 215), "The derivative lives on the curve");
size(headline, 39); color(headline, fg); bold(headline); hidden(headline);

rect(stage, (540, 855), 930, 1050);
color(stage, panel); outline(stage, dim); opacity(stage, 0.72);

equation(work, (540, 390), `f(x)=0.35x^2`, 51);
color(work, cyan); hidden(work);

let ox = 540;
let oy = 1050;
let sx = 115;
let sy = 62;

axes(ax, (ox, oy), 410, 420, 1);
color(ax, dim); opacity(ax, 0.55); untraced(ax);

plot(f, (ox, oy), sx, sy, "0.35*x*x", (-3.5, 3.5));
color(f, cyan); stroke(f, 6); glow(f, 0.8); untraced(f);

deriv(df, f);
color(df, magenta); stroke(df, 5); dashed(df, 18, 11); untraced(df);

tangent(tan, f, -2.8, 235);
color(tan, gold); stroke(tan, 4); hidden(tan);

slope(rate, f, -2.8, (24, -30));
color(rate, gold); hidden(rate);

dot(vertex, (ox, oy), 10);
color(vertex, lime); glow(vertex, 1.8); hidden(vertex);

equation(curveLabel, (790, 650), `f(x)`, 31);
color(curveLabel, cyan); hidden(curveLabel);

equation(derivLabel, (790, 1320), `f'(x)`, 31);
color(derivLabel, magenta); hidden(derivLabel);

text(caption, (540, 1450), "Start with one curve and one moving question: how steep is it?");
size(caption, 26); color(caption, dim); wrap(caption, 820); hidden(caption);

// ---------------------------------------------------------------------------
// NAMED REACTIVE STEPS
// ---------------------------------------------------------------------------

step("question") {
  show(kicker, 0.40);
  show(headline, 0.50);
  show(work, 0.55);
  draw(ax, 0.85);
  draw(f, 1.40);
  show(curveLabel, 0.45);
  show(caption, 0.45);
}
wait(0.75);

step("measure-slope") {
  rewrite(work, `f'(x)=0.70x`, 0.90, smooth);
  show(tan, 0.45);
  show(rate, 0.45);
  to(tan, x, 2.8, 3.20, smooth);
  to(rate, x, 2.8, 3.20, smooth);
  say(caption, "Move the point: the tangent and its slope update together.", 0.40);
}
wait(0.60);

step("find-the-flat-point") {
  rewrite(work, `f'(0)=\textcolor{lime}{0}`, 0.85, smooth);
  to(tan, x, 0, 1.80, smooth);
  to(rate, x, 0, 1.80, smooth);
  show(vertex, 0.40);
  say(caption, "At the vertex the tangent becomes flat, so the derivative is zero.", 0.40);
}
wait(0.65);

step("see-the-derivative") {
  rewrite(work, `f'(x)=\textcolor{magenta}{0.70x}`, 0.90, smooth);
  fade(tan, 0.40);
  fade(rate, 0.40);
  draw(df, 1.70);
  show(derivLabel, 0.45);
  say(caption, "Collect every local slope and a second curve appears: the derivative.", 0.40);
}
wait(0.70);

step("takeaway") {
  rewrite(work, `\text{slope of }f=\textcolor{magenta}{f'}`, 0.95, smooth);
  pulse(f, 0.70);
  pulse(df, 0.70);
  say(caption, "One idea, synchronized across symbols, geometry and motion.", 0.40);
}
wait(1.80);

sine_wave

axes + plot, a curve traced on, then vectors.

// The Sine Wave — a first taste of the manic math kit.
//   manic examples/sine_wave.manic
//   manic examples/sine_wave.manic --still 2.6 --scale 1.5 --crt

title("The Sine Wave");
canvas(1280, 720);

// --- cast: the world at t = 0 ---

// a coordinate frame centred on the stage
axes(ax, (640, 380), 520, 240);
text(xlab, (1180, 410), "x");  color(xlab, dim);  size(xlab, 22);
text(ylab, (665, 152), "y");   color(ylab, dim);  size(ylab, 22);

// the curve: visible but not yet drawn, so we can trace it on
plot(wave, (640, 380), 78, 120, sin, 6.6);
untraced(wave);

// a vector to point at, revealed later
vector(v1, (640, 380), (122, 108));
hidden(v1);

// headline + caption
text(head, (640, 118), "y = sin(x)");
display(head);  color(head, cyan);  size(head, 40);  hidden(head);
text(cap, (640, 662), "");  color(cap, dim);  size(cap, 22);

// --- script: beats, top to bottom ---

show(head, 0.5);
say(cap, "a coordinate frame on the void");
draw(wave, 1.7);
say(cap, "y = sin(x), traced on");
wait(0.6);

section("Vectors");
say(cap, "a vector from the origin");
par {
  show(v1, 0.4);
  pulse(v1);
}
wait(1.2);

function_graph

Plot an expression straight from a formula string.

// Function Graphs — plot ANY formula, not just a named curve. manic's answer to
// Manim's FunctionGraph(lambda t: ...): pass a formula string in x (alias t) and
// plot() samples it. This reproduces Manim's ExampleFunctionGraph — two
// Fourier-style packets and a domain-clipped, lifted copy of the second.
//
//   manic examples/function_graph.manic
//   manic examples/function_graph.manic --record out --fps 60

title("Function Graphs");
canvas(1280, 720);

text(head, (640, 92), "plot any formula — y = f(x)");
display(head);  color(head, cyan);  size(head, 26);  hidden(head);
text(cap, (640, 656), "");  color(cap, dim);  size(cap, 22);

// a faint frame to read the curves against (unit = 70 px)
plane(pl, (640, 384), 620, 300, 70);
hidden(pl.grid);  untraced(pl.x);  untraced(pl.y);

// a cosine packet: cos t + 1/2 cos 7t + 1/7 cos 14t, over x in [-7, 7]
plot(cosf, (640, 384), 70, 70, "cos(x) + 0.5*cos(7*x) + (1/7)*cos(14*x)", 7);
color(cosf, magenta);  untraced(cosf);

// the sine version of the same packet
plot(sinf, (640, 384), 70, 70, "sin(x) + 0.5*sin(7*x) + (1/7)*sin(14*x)", 7);
color(sinf, cyan);  untraced(sinf);

// same formula, clipped to x in [-4, 4] and lifted one unit (centre y - 70)
plot(sinf2, (640, 314), 70, 70, "sin(x) + 0.5*sin(7*x) + (1/7)*sin(14*x)", 4);
color(sinf2, lime);  untraced(sinf2);

// --- reveal ---
show(head, 0.5);
section("The plane");
say(cap, "a grid to read against — arrows on the axes");
show(pl.grid, 0.6);
par { draw(pl.x, 0.5);  draw(pl.y, 0.5); }
wait(0.3);

section("A cosine packet");
say(cap, "y = cos t + 1/2 cos 7t + 1/7 cos 14t");
draw(cosf, 1.3);
wait(0.6);

section("A sine packet");
say(cap, "same shape, sin for cos");
draw(sinf, 1.3);
wait(0.6);

section("Clip the domain");
say(cap, "same formula, but only x in [-4, 4], lifted one unit");
draw(sinf2, 1.1);
par { pulse(cosf);  pulse(sinf);  pulse(sinf2); }
wait(1.4);

area_under_curve

Riemann rectangles sweeping to the integral.

// Area Under a Curve — a Riemann sum sweeping n = 5, 10, 20, 40 to show the
// rectangles converging to the exact integral of x^2 on [0, 2.5] = 125/24.
//
// This is the FIRST example to use manic's loop layer: `let` variables,
// arithmetic in arguments, a `for` range loop, and id interpolation (`s5{i}`).
// The four bar-sets differ only in n / prefix / colour — a future `def` macro
// would collapse them to one call; loops already do the per-bar work.
//
//   manic examples/area_under_curve.manic
//   manic examples/area_under_curve.manic --record out --fps 60

title("Area Under a Curve");
canvas(1280, 720);

// --- parameters (edit freely) ---
let ox = 360;   let oy = 590;     // origin, in screen px
let ux = 200;   let uy = 52;      // px per unit on each axis
let a = 0;      let b = 2.5;      // integrate x^2 over [a, b]

text(head, (640, 96), "a Riemann sum becomes an integral");
display(head);  color(head, cyan);  size(head, 26);  hidden(head);
text(cap, (640, 656), "");  color(cap, dim);  size(cap, 24);

// axes
arrow(xax, (ox - 40, oy), (920, oy));   color(xax, dim);  untraced(xax);
arrow(yax, (ox, oy + 20), (ox, 250));   color(yax, dim);  untraced(yax);
text(t1, (ox + 1*ux, oy + 24), "1");     color(t1, dim);  size(t1, 18);
text(t2, (ox + 2*ux, oy + 24), "2");     color(t2, dim);  size(t2, 18);
text(tb, (ox + b*ux, oy + 24), "2.5");   color(tb, dim);  size(tb, 18);

// the curve y = x^2 over [0, 2.5]
plot(curve, (ox, oy), ux, uy, "x*x", (a, b));  color(curve, cyan);  z(curve, 3);  untraced(curve);
text(clab, (ox + b*ux + 30, oy - b*b*uy), "y = x^2");  color(clab, cyan);  size(clab, 22);  hidden(clab);

// --- midpoint rectangles, one loop per count ---
let n = 5;   let dx = (b - a) / n;
for i in 0..n {
  let mid = a + (i + 0.5) * dx;   let h = mid * mid;
  rect(s5{i}, (ox + mid*ux, oy - h*uy/2), dx*ux, h*uy);
  filled(s5{i});  color(s5{i}, magenta);  opacity(s5{i}, 0.4);  tag(s5{i}, r5);
}
let n = 10;  let dx = (b - a) / n;
for i in 0..n {
  let mid = a + (i + 0.5) * dx;   let h = mid * mid;
  rect(s10{i}, (ox + mid*ux, oy - h*uy/2), dx*ux, h*uy);
  filled(s10{i});  color(s10{i}, magenta);  opacity(s10{i}, 0.4);  tag(s10{i}, r10);
}
let n = 20;  let dx = (b - a) / n;
for i in 0..n {
  let mid = a + (i + 0.5) * dx;   let h = mid * mid;
  rect(s20{i}, (ox + mid*ux, oy - h*uy/2), dx*ux, h*uy);
  filled(s20{i});  color(s20{i}, magenta);  opacity(s20{i}, 0.4);  tag(s20{i}, r20);
}
let n = 40;  let dx = (b - a) / n;
for i in 0..n {
  let mid = a + (i + 0.5) * dx;   let h = mid * mid;
  rect(s40{i}, (ox + mid*ux, oy - h*uy/2), dx*ux, h*uy);
  filled(s40{i});  color(s40{i}, magenta);  opacity(s40{i}, 0.4);  tag(s40{i}, r40);
}
hidden(r5);  hidden(r10);  hidden(r20);  hidden(r40);

// --- script ---
show(head, 0.5);
say(cap, "the shaded area under y = x^2 from 0 to 2.5");
par { draw(xax, 0.5);  draw(yax, 0.5); }
draw(curve, 0.9);
show(clab, 0.3);
wait(0.4);

section("Rectangles");
say(cap, "n = 5 rectangles  ->  area ~ 5.16");
show(r5, 0.6);
wait(0.7);
fade(r5, 0.3);

say(cap, "n = 10  ->  area ~ 5.20");
show(r10, 0.5);
wait(0.6);
fade(r10, 0.3);

say(cap, "n = 20  ->  area ~ 5.20");
show(r20, 0.5);
wait(0.6);
fade(r20, 0.3);

say(cap, "n = 40  ->  area ~ 5.21 (hugging the curve)");
show(r40, 0.5);
wait(0.8);

section("The integral");
say(cap, "as n grows without bound, the sum IS the integral");
fade(r40, 0.4);
text(ans, (640, 300), "exact area = 125/24 = 5.208");  display(ans);  color(ans, lime);  size(ans, 30);  hidden(ans);
show(ans, 0.5);
pulse(ans);
wait(1.6);

riemann_rainbow

Coloured Riemann rectangles revealed one by one.

// Riemann Rainbow — the area under y = sin(x) on [0, pi], sliced into rectangles
// that each get their own neon hue and rise into place one by one, left to right.
//
// A showcase for the loop layer: one `for` builds all the bars (each `hue`d by
// its index), and a `stagger` block sweeps them in. Exact area = 2.
//
//   manic examples/riemann_rainbow.manic
//   manic examples/riemann_rainbow.manic --record out --fps 60

title("Riemann Rainbow");
canvas(1280, 720);

// --- parameters ---
let ox = 190;   let oy = 560;      // origin (screen px)
let ux = 300;   let uy = 340;      // px per unit
let a = 0;      let b = pi;         // y = sin(x) over [0, pi]
let n = 28;     let dx = (b - a) / n;

text(head, (640, 96), "area under y = sin(x), one slice at a time");
display(head);  color(head, cyan);  size(head, 26);  hidden(head);
text(cap, (640, 640), "");  color(cap, dim);  size(cap, 24);

// axes
arrow(xax, (ox - 40, oy), (1180, oy));   color(xax, dim);  untraced(xax);
arrow(yax, (ox, oy + 20), (ox, 180));    color(yax, dim);  untraced(yax);
text(l0, (ox, oy + 26), "0");            color(l0, dim);  size(l0, 18);
text(lp, (ox + b*ux, oy + 26), "pi");    color(lp, dim);  size(lp, 18);

// the curve
plot(curve, (ox, oy), ux, uy, "sin(x)", (a, b));  color(curve, fg);  z(curve, 5);  untraced(curve);

// --- one rainbow bar per slice (midpoint heights) ---
for i in 0..n {
  let mid = a + (i + 0.5) * dx;
  let h = sin(mid);
  rect(bar{i}, (ox + mid*ux, oy - h*uy/2), dx*ux, h*uy);
  filled(bar{i});
  hue(bar{i}, 360 * i / n);      // each slice its own colour
  opacity(bar{i}, 0.9);
  tag(bar{i}, bars);
}
hidden(bars);

// --- script ---
show(head, 0.5);
say(cap, "y = sin(x) from 0 to pi");
par { draw(xax, 0.5);  draw(yax, 0.5); }
draw(curve, 1.0);
wait(0.3);

section("Slice by slice");
say(cap, "28 rectangles rise in, left to right");
stagger(0.05) {
  for i in 0..n { show(bar{i}, 0.35); }
}
wait(0.6);

section("The area");
say(cap, "together they fill the area under the curve = 2");
par { pulse(curve); }
wait(1.6);

riemann_readout

Running sums shown as a live computed number.

// Riemann + Live Total — the midpoint area under y = x^2 on [0, 2.5] is
// COMPUTED in-language with a `sum(...)` reduction, and a `counter` readout
// tweens from 0 up to that total while the bars fill in. The number you see
// counting is the reduction's value.
//
// Showcases reductions + animated numeric readouts (`counter` + `to(_, value)`).
//
//   manic examples/riemann_readout.manic
//   manic examples/riemann_readout.manic --record out --fps 60

title("Riemann + Live Total");
canvas(1280, 720);

let ox = 300;   let oy = 560;
let ux = 190;   let uy = 52;
let a = 0;      let b = 2.5;   let n = 40;   let dx = (b - a) / n;

// the midpoint Riemann sum, computed at build time
let area = sum(i in 0..n : (a + (i + 0.5)*dx)^2 * dx);

text(head, (640, 90), "area under y = x^2, summed as the bars fill");
display(head);  color(head, cyan);  size(head, 26);  hidden(head);

counter(total, (950, 210), 0, 3, "area = ", "");
display(total);  color(total, lime);  size(total, 36);  hidden(total);
text(exact, (950, 260), "exact 125/24 = 5.208");  color(exact, dim);  size(exact, 20);  hidden(exact);

// axes
arrow(xax, (ox - 40, oy), (900, oy));   color(xax, dim);  untraced(xax);
arrow(yax, (ox, oy + 20), (ox, 210));   color(yax, dim);  untraced(yax);
text(t1, (ox + 1*ux, oy + 24), "1");    color(t1, dim);  size(t1, 18);
text(t2, (ox + 2*ux, oy + 24), "2");    color(t2, dim);  size(t2, 18);

// curve
plot(curve, (ox, oy), ux, uy, "x*x", (a, b));  color(curve, cyan);  z(curve, 4);  untraced(curve);

// midpoint rectangles
for i in 0..n {
  let mid = a + (i + 0.5) * dx;
  let h = mid * mid;
  rect(bar{i}, (ox + mid*ux, oy - h*uy/2), dx*ux, h*uy);
  filled(bar{i});  color(bar{i}, magenta);  opacity(bar{i}, 0.45);  tag(bar{i}, bars);
}
hidden(bars);

// --- script ---
show(head, 0.5);
par { draw(xax, 0.5);  draw(yax, 0.5); }
draw(curve, 0.9);
show(total, 0.3);
show(exact, 0.3);
wait(0.3);

// bars sweep in while the total counts up to the reduction's value
par {
  stagger(0.03) { for i in 0..n { show(bar{i}, 0.25); } }
  to(total, value, area, 1.6, linear);
}
wait(1.4);
pulse(total);
wait(1.0);

tangent

The tangent line to a curve, sliding along it — its tilt is read from the function itself, so it’s always the true slope (flat at the peaks).

title("The Tangent Line — a curve's slope, made visible");
canvas("16:9");

text(hdr, (cx, 60), "The slope of sin x, everywhere at once");
size(hdr, 30); color(hdr, cyan); bold(hdr); display(hdr); hidden(hdr);

// the curve — plot remembers its function, so tangent can ask it questions.
// center the domain 0..6.3 across the canvas (midpoint x=3.15 sits at cx)
plot(f, (cx - 440, cy + 40), 140, 120, "sin(x)", (0, 6.3));
color(f, cyan); stroke(f, 3); untraced(f);

// a tangent that touches f where x = 0.5 (line + contact dot, one call)
tangent(tan, f, 0.5, 260);
color(tan, gold); untraced(tan);

text(cap, (cx, h - 60), "watch the tilt follow the curve");
size(cap, 20); color(cap, dim); display(cap); hidden(cap);

// ---- timeline ----
show(hdr, 0.5);
draw(f, 1.4);
show(tan, 0.6);
show(cap, 0.4);
wait(0.4);

// slide the touch point along the whole curve — the slope is derived live
to(tan, x, 5.8, 5);
wait(0.3);
to(tan, x, 1.6, 2.5);   // back toward the first peak, where the slope is 0

analysis

Ask one curve everything at once — tangent, a live slope number, the normal, the area sweeping open beneath it, and the integral climbing to its true value.

title("Reading a curve: slope, tangent, normal, area");
canvas("16:9");

text(hdr, (cx, 58), "Asking a curve questions");
size(hdr, 30); color(hdr, cyan); bold(hdr); display(hdr); hidden(hdr);

// one curve — everything below asks IT, by name
plot(f, (cx - 440, cy + 30), 140, 110, "sin(x)", (0, 6.3));
color(f, cyan); stroke(f, 3); untraced(f);

// the area under it — starts collapsed at x=0.6, sweeps open
area(reg, f, 0.6, 0.6);
color(reg, lime);

// a live integral readout that climbs as the area sweeps
integral(area_val, f, 0.6, 0.6, (cx + 340, cy - 210));
color(area_val, lime);

// tangent + its live slope number, both riding the same point
tangent(tan, f, 0.6, 240);
color(tan, gold);

slope(sl, f, 0.6);
color(sl, gold);

// the normal, perpendicular at the same place
normal(nrm, f, 0.6, 150);
color(nrm, magenta); hidden(nrm);

// ---- timeline ----
show(hdr, 0.5);
draw(f, 1.4);
show(tan, 0.5);
show(sl, 0.4);
wait(0.4);

// sweep the area open while the tangent + slope slide together and the
// integral readout climbs
par {
  to(reg, x, 5.7, 4);
  to(area_val, x, 5.7, 4);
  to(tan, x, 5.7, 4);
  to(sl, x, 5.7, 4);
}
wait(0.4);

// reveal the normal and glide everything back to the first peak (slope 0)
show(nrm, 0.4);
par {
  to(tan, x, 1.571, 2.5);
  to(sl, x, 1.571, 2.5);
  to(nrm, x, 1.571, 2.5);
}

newton

Newton’s method, drawn as a zig-zag: from a first guess, slide down each tangent to the axis, back up to the curve, and watch the guesses walk to the root.

title("Newton's method: chasing a root");
canvas("16:9");

text(hdr, (cx, 56), "Newton's method finds where x squared = 2");
size(hdr, 28); color(hdr, cyan); bold(hdr); display(hdr); hidden(hdr);

// a reference frame so the x-axis (where the tangents land) is visible
axes(ax, (cx - 250, cy + 170), 540, 360, 1);
color(ax, dim); untraced(ax);

// f(x) = x^2 - 2, whose positive root is sqrt(2)
plot(f, (cx - 250, cy + 170), 180, 70, "x*x - 2", (-0.4, 3));
color(f, cyan); stroke(f, 3); untraced(f);

// the true zero-crossing, marked
roots(zero, f);
color(zero, lime); hidden(zero);

// the Newton zig-zag from a first guess of x = 3
newton(walk, f, 3);
color(walk, gold); stroke(walk, 3); untraced(walk);

text(cap, (cx, h - 60), "each tangent slides the guess closer");
size(cap, 20); color(cap, dim); display(cap); hidden(cap);

// ---- timeline ----
show(hdr, 0.5);
draw(ax, 0.8);
draw(f, 1.4);
wait(0.3);
show(cap, 0.4);

// walk the iterates in to the root
draw(walk, 3.5);
wait(0.3);

// reveal the exact root it converged on
show(zero, 0.5);
pulse(zero, 0.6);

inverse-derivatives

Why a function and its inverse have reciprocal slopes: e^x and ln x mirrored across y = x, with the slopes at matching points multiplying to 1.

title("Inverse-function derivatives — why the pairs mirror");
canvas("16:9");

// uniform scale (sx == sy) so a function and its inverse reflect correctly
let ox = cx - 470;
let oy = cy + 110;
let s = 72;

text(hdr, (cx, 52), "A function and its inverse: mirror curves, reciprocal slopes");
size(hdr, 26); color(hdr, cyan); bold(hdr); display(hdr); hidden(hdr);

// ---------------- the mirror plane ----------------
axes(ax, (ox, oy), 470, 300, 1);
color(ax, dim); untraced(ax);

// y = x, the mirror line
line(diag, (ox - 2.6*s, oy + 2.6*s), (ox + 3.8*s, oy - 3.8*s));
color(diag, dim); untraced(diag);
text(diaglab, (ox + 3.4*s, oy - 3.4*s - 22), "y = x");
size(diaglab, 16); color(diaglab, dim); display(diaglab); hidden(diaglab);

// e^x and its inverse ln x — reflections of each other
plot(ef, (ox, oy), s, s, "exp(x)", (-2.6, 1.45));
color(ef, cyan); stroke(ef, 3); untraced(ef);
text(eflab, (ox + 1.45*s + 8, oy - 4.1*s), "e^x");
size(eflab, 18); color(eflab, cyan); display(eflab); hidden(eflab);

plot(lf, (ox, oy), s, s, "ln(x)", (0.075, 4.3));
color(lf, magenta); stroke(lf, 3); untraced(lf);
text(lflab, (ox + 4.2*s, oy - 1.45*s), "ln x");
size(lflab, 18); color(lflab, magenta); display(lflab); hidden(lflab);

// tangent + live slope at a point on e^x, and at its mirror point on ln x
tangent(te, ef, 0.5, 150);
color(te, cyan); hidden(te);
slope(se, ef, 0.5);
color(se, cyan); hidden(se);

tangent(tl, lf, 1.65, 150);
color(tl, magenta); hidden(tl);
slope(sl, lf, 1.65);
color(sl, magenta); hidden(sl);

text(rec, (cx + 340, cy + 60), "slope x slope = 1");
size(rec, 22); color(rec, lime); bold(rec); display(rec); hidden(rec);
text(rec2, (cx + 340, cy + 96), "(1.65) x (0.61)");
size(rec2, 18); color(rec2, dim); display(rec2); hidden(rec2);

// ---------------- the four identity pairs ----------------
text(t1, (cx + 340, cy - 190), "(e^x)' = e^x        (ln x)' = 1/x");
size(t1, 17); color(t1, fg); display(t1); hidden(t1);
text(t2, (cx + 340, cy - 150), "(sin x)' = sqrt(1-sin^2 x)   (arcsin x)' = 1/sqrt(1-x^2)");
size(t2, 15); color(t2, fg); display(t2); hidden(t2);
text(t3, (cx + 340, cy - 118), "(cos x)' = -sqrt(1-cos^2 x)  (arccos x)' = -1/sqrt(1-x^2)");
size(t3, 15); color(t3, fg); display(t3); hidden(t3);
text(t4, (cx + 340, cy - 86), "(tan x)' = 1+tan^2 x         (arctan x)' = 1/(1+x^2)");
size(t4, 15); color(t4, fg); display(t4); hidden(t4);

// ================= timeline =================
show(hdr, 0.6);
draw(ax, 0.7);
draw(diag, 0.5);
show(diaglab, 0.3);
par {
  draw(ef, 1.4);
  draw(lf, 1.4);
}
par {
  show(eflab, 0.4);
  show(lflab, 0.4);
}
wait(0.3);

// the reciprocal-slope check
par {
  show(te, 0.4);
  show(se, 0.4);
}
par {
  show(tl, 0.4);
  show(sl, 0.4);
}
show(rec, 0.5);
show(rec2, 0.4);
flash(rec, lime);
wait(0.4);

// the four pairs it generates
stagger(0.4) {
  show(t1, 0.4);
  show(t2, 0.4);
  show(t3, 0.4);
  show(t4, 0.4);
}

derivative-of-ln-x

A visual proof of (ln x)' = 1/x: one continuous 3D coordinate plane turns over, x/y exchange screen roles, and unchanged algebra terms retain their identity.

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

spline

Interpolation: one smooth curve drawn through a scattered set of points — it passes through every knot exactly.

title("A smooth curve through the data");
canvas("16:9");

text(hdr, (cx, 70), "Interpolation: one smooth curve through every point");
size(hdr, 26); color(hdr, cyan); bold(hdr); display(hdr); hidden(hdr);

// scattered data points — the spline passes through all of them
spline(s, (300, 620), (520, 340), (760, 560), (1000, 300), (1240, 480), (1500, 260));
color(s, cyan); stroke(s, 4); untraced(s);

text(cap, (cx, h - 60), "Catmull-Rom — it hits each knot exactly");
size(cap, 20); color(cap, dim); display(cap); hidden(cap);

// ---- timeline ----
show(hdr, 0.5);
// pop the knots in first
show(s.knots, 0.6);
wait(0.3);
// then trace the smooth curve through them
draw(s, 2.2);
show(cap, 0.4);

trajectory

A phase portrait: three paths flowing under a differential system, each spiralling into the sink at the origin.

title("Phase portrait: flowing into a spiral sink");
canvas("16:9");

text(hdr, (cx, 60), "dx/dt = -y - 0.2x,   dy/dt = x - 0.2y");
size(hdr, 26); color(hdr, cyan); bold(hdr); display(hdr); hidden(hdr);

// a reference frame centered on the sink
axes(ax, (cx, cy + 20), 620, 360, 1);
color(ax, dim); untraced(ax);

// three trajectories from different starts, each spiraling into the origin
trajectory(t1, "-y - 0.2*x", "x - 0.2*y", (3.2, 0), (cx, cy + 20), 110, 520);
color(t1, cyan); stroke(t1, 3); untraced(t1);

trajectory(t2, "-y - 0.2*x", "x - 0.2*y", (0, 3.4), (cx, cy + 20), 110, 520);
color(t2, magenta); stroke(t2, 3); untraced(t2);

trajectory(t3, "-y - 0.2*x", "x - 0.2*y", (-3.0, -2.4), (cx, cy + 20), 110, 520);
color(t3, gold); stroke(t3, 3); untraced(t3);

// ---- timeline ----
show(hdr, 0.5);
draw(ax, 0.8);
par {
  draw(t1, 3.5);
  draw(t2, 3.5);
  draw(t3, 3.5);
}

band

The area trapped between two curves, filled directly with band(top,bottom) while both boundary plots remain visible.

title("The area between two curves");
canvas("16:9");

let ox = cx - 460;
let oy = cy + 40;

text(hdr, (cx, 58), "The region trapped between two curves");
size(hdr, 26); color(hdr, cyan); bold(hdr); display(hdr); hidden(hdr);

axes(ax, (ox, oy), 900, 260, 1);
color(ax, dim); untraced(ax);

// two curves over the same stretch of x
plot(upper, (ox, oy), 140, 90, "0.4*x + 1.4", (0, 6.3));
color(upper, cyan); stroke(upper, 3); untraced(upper);

plot(lower, (ox, oy), 140, 90, "sin(x)", (0, 6.3));
color(lower, magenta); stroke(lower, 3); untraced(lower);

// the band between them
band(gap, upper, lower);
color(gap, lime); hidden(gap);

text(cap, (cx, h - 56), "band(top, bottom) fills the space between");
size(cap, 20); color(cap, dim); display(cap); hidden(cap);

// ---- timeline ----
show(hdr, 0.6);
draw(ax, 0.7);
par {
  draw(upper, 1.4);
  draw(lower, 1.4);
}
wait(0.3);
to(gap, opacity, 0.3, 0.6);   // fade in to translucent (so the curves read through)
show(cap, 0.4);

curve-features

Read a cubic by its geometry: maxima/minima where the slope is zero and an inflection where the curve changes its bend (extrema, inflections).

title("Reading a curve's shape — maxima, minima, inflection");
canvas("16:9");

let ox = cx - 540;   // math x=0 maps here (centres the domain 1..5 under cx)
let oy = cy;

text(hdr, (cx, 56), "Where the curve turns, and where it changes its bend");
size(hdr, 26); color(hdr, cyan); bold(hdr); display(hdr); hidden(hdr);

axes(ax, (ox, oy), 940, 240, 1);
color(ax, dim); untraced(ax);

// a cubic S-curve: one maximum, one minimum, one inflection
plot(f, (ox, oy), 180, 170, "0.4*((x-3)*(x-3)*(x-3) - 4*(x-3))", (1, 5));
color(f, cyan); stroke(f, 4); untraced(f);

// maxima & minima (slope = 0)
extrema(turn, f);
color(turn, gold);

// inflection (concavity flips, f'' = 0)
inflections(bend, f);
color(bend, magenta);

text(lg, (cx + 300, 150), "gold: max / min  (slope 0)");
size(lg, 18); color(lg, gold); display(lg); hidden(lg);
text(lm, (cx + 300, 186), "pink: inflection  (bend flips)");
size(lm, 18); color(lm, magenta); display(lm); hidden(lm);

// ---- timeline ----
show(hdr, 0.6);
draw(ax, 0.7);
draw(f, 1.6);
wait(0.3);
show(turn, 0.5);
show(lg, 0.4);
pulse(turn, 0.6);
wait(0.3);
show(bend, 0.5);
show(lm, 0.4);
pulse(bend, 0.6);

ftc

The Fundamental Theorem of Calculus: accumulate the area under a curve, differentiate that area function, and watch the original function return.

title("The Fundamental Theorem of Calculus");
canvas("16:9");

let ox = cx - 460;   // math x=0 maps here
let oy = cy + 20;

text(hdr, (cx, 52), "Differentiate the area — and the function comes back");
size(hdr, 26); color(hdr, cyan); bold(hdr); display(hdr); hidden(hdr);

axes(ax, (ox, oy), 900, 200, 1);
color(ax, dim); untraced(ax);

// f(x) = cos x
plot(f, (ox, oy), 140, 150, "cos(x)", (0, 6.3));
color(f, cyan); stroke(f, 4); untraced(f);

// F(x) = area under f from 0 to x  =  sin x
accum(bigF, f);
color(bigF, lime); stroke(bigF, 3); untraced(bigF);

// F'(x): differentiate the area function — it lands right back on f
deriv(dF, bigF);
color(dF, gold); stroke(dF, 3); untraced(dF);

// legend
text(lf, (cx + 300, 150), "f(x) = cos x");
size(lf, 20); color(lf, cyan); display(lf); hidden(lf);
text(lF, (cx + 300, 190), "F(x) = area so far");
size(lF, 20); color(lF, lime); display(lF); hidden(lF);
text(ld, (cx + 300, 230), "F'(x) = f(x)");
size(ld, 20); color(ld, gold); bold(ld); display(ld); hidden(ld);

text(cap, (cx, h - 52), "the slope of the area-so-far IS the original curve");
size(cap, 20); color(cap, dim); display(cap); hidden(cap);

// ================= timeline =================
show(hdr, 0.6);
draw(ax, 0.7);

// 1. the function
draw(f, 1.3);
show(lf, 0.4);
wait(0.3);

// 2. its accumulated area, as a new curve (this is sin x)
draw(bigF, 1.6);
show(lF, 0.4);
wait(0.4);

// 3. differentiate that area function — it traces back onto f
draw(dF, 1.6);
show(ld, 0.4);
flash(dF, gold);
show(cap, 0.5);

limit

A removable discontinuity visualized as an approaching point, open circle and live finite limit at x→0.

title("A limit — the value a curve heads toward");
canvas("16:9");

let ox = cx - 120;
let oy = cy;

text(hdr, (cx, 54), "sin(x) / x is undefined at 0 — but it heads straight for 1");
size(hdr, 24); color(hdr, cyan); bold(hdr); display(hdr); hidden(hdr);

axes(ax, (ox, oy), 620, 300, 1);
color(ax, dim); untraced(ax);

// sin(x)/x — a removable hole at x = 0, where the limit is 1
plot(f, (ox, oy), 150, 220, "sin(x)/x", (-6.2, 6.2));
color(f, cyan); stroke(f, 4); untraced(f);

// the limit at x -> 0: open circle at (0, 1), guides, value, and an approaching dot
limit(lim, f, 0);
color(lim, gold);

text(cap, (cx, h - 54), "slide the point in — f(x) closes on the open circle");
size(cap, 20); color(cap, dim); display(cap); hidden(cap);

// ---- timeline ----
show(hdr, 0.6);
draw(ax, 0.7);
draw(f, 1.6);
wait(0.3);
show(lim, 0.5);
show(cap, 0.4);
wait(0.3);
// walk the point toward x = 0 from the left — it approaches the open circle
to(lim, x, 0, 3.5);

limit-infinity

A rational function settling onto its horizontal asymptote, with limit(...,inf) detecting and marking the value at infinity.

title("A limit at infinity — limit(id, curve, inf)");
canvas("16:9");

let ox = 90;          // x = 0 near the left
let oy = cy + 250;    // y = 0 baseline

text(hdr, (cx, 52), "(5x^3 - 2x + 7) / (x^3 + 4x^2 + 3)  ->  5  as x -> infinity");
size(hdr, 24); color(hdr, cyan); bold(hdr); display(hdr); hidden(hdr);

text(sub, (cx, 92), "same degree top and bottom: the ratio of leading coefficients, 5/1");
size(sub, 17); color(sub, dim); display(sub); hidden(sub);

// the x-axis (y = 0)
line(xaxis, (ox, oy), (ox + 1130, oy));
color(xaxis, dim); untraced(xaxis);

// the rational function, climbing toward its asymptote
plot(f, (ox, oy), 9, 88, "(5*x*x*x - 2*x + 7)/(x*x*x + 4*x*x + 3)", (0.5, 125));
color(f, cyan); stroke(f, 4); untraced(f);

// the limit at infinity: auto-detects the horizontal asymptote y = 5
limit(lim, f, inf);
color(lim, gold); hidden(lim);

// ---- timeline ----
show(hdr, 0.6);
show(sub, 0.4);
draw(xaxis, 0.5);
draw(f, 2.4);
show(lim, 0.7);

taylor

Taylor polynomials of increasing degree closing in on sin(x), one additional approximation at a time.

title("Taylor series — polynomials closing in on a curve");
canvas("16:9");

let ox = cx;         // x=0 at centre
let oy = cy + 10;

text(hdr, (cx, 52), "Add one more term, and the polynomial hugs more of the curve");
size(hdr, 24); color(hdr, cyan); bold(hdr); display(hdr); hidden(hdr);

axes(ax, (ox, oy), 640, 260, 1);
color(ax, dim); untraced(ax);

// the target function
plot(f, (ox, oy), 95, 120, "sin(x)", (-6.2, 6.2));
color(f, cyan); stroke(f, 5); untraced(f);

// Taylor polynomials about x = 0, growing in degree
taylor(p1, f, 0, 1);
color(p1, dim); stroke(p1, 3); untraced(p1);
taylor(p3, f, 0, 3);
color(p3, gold); stroke(p3, 3); untraced(p3);
taylor(p5, f, 0, 5);
color(p5, magenta); stroke(p5, 3); untraced(p5);
taylor(p7, f, 0, 7);
color(p7, lime); stroke(p7, 3); untraced(p7);

text(l1, (cx + 300, 150), "n = 1   (a line)");
size(l1, 18); color(l1, dim); display(l1); hidden(l1);
text(l3, (cx + 300, 186), "n = 3");
size(l3, 18); color(l3, gold); display(l3); hidden(l3);
text(l5, (cx + 300, 222), "n = 5");
size(l5, 18); color(l5, magenta); display(l5); hidden(l5);
text(l7, (cx + 300, 258), "n = 7");
size(l7, 18); color(l7, lime); display(l7); hidden(l7);

// ---- timeline ----
show(hdr, 0.6);
draw(ax, 0.7);
draw(f, 1.6);
wait(0.3);
draw(p1, 0.8);  show(l1, 0.3);  wait(0.3);
draw(p3, 0.9);  show(l3, 0.3);  wait(0.3);
draw(p5, 1.0);  show(l5, 0.3);  wait(0.3);
draw(p7, 1.1);  show(l7, 0.3);