Calculus & functions
Each block is the whole file — copy it into x.manic and run manic x.manic (live) or --record out (video).
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);