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

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