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

Linear algebra & tables

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

matrix

A bracketed matrix, rows/columns addressable via tags.

// Matrix — a bracketed grid of entries, addressable by row and column via tag
// broadcast (à la Manim's Matrix + set_row_colors / set_column_colors).
//
//   manic examples/matrix.manic
//   manic examples/matrix.manic --record out --fps 60
//
// Rows are separated by ';', entries by spaces/commas. Entry ids m.r{i}c{j};
// tags m.row{i} / m.col{j} / m.entries.

title("Matrix");
canvas(1280, 720);

text(head, (640, 130), "rows and columns you can address");
display(head);  color(head, cyan);  size(head, 28);  hidden(head);
text(cap, (640, 620), "");  color(cap, dim);  size(cap, 22);

matrix(m, "2 0 4; -1 1 5; 3 -2 0", (640, 370));
untraced(m.lbrack);  untraced(m.rbrack);
hidden(m.entries);

show(head, 0.5);
say(cap, "a 3x3 matrix");
par { draw(m.lbrack, 0.5);  draw(m.rbrack, 0.5); }
seq { show(m.row0, 0.35);  show(m.row1, 0.35);  show(m.row2, 0.35); }
wait(0.5);

section("Columns");
say(cap, "colour a column — set_column_colors");
recolor(m.col1, magenta, 0.4);
flash(m.col2, cyan);
wait(0.5);

section("Rows");
say(cap, "and highlight a row — set_row_colors");
recolor(m.row0, lime, 0.4);
par { pulse(m.r0c0);  pulse(m.r0c1);  pulse(m.r0c2); }
wait(1.2);

matrix_addition

Two matrices summed, cell by cell.

// Matrix Addition — A + B = C, computed entry by entry. Each matching pair of
// entries flashes, then their sum pops into the result matrix. A teaching
// animation: it shows *why* matrix addition is element-wise.
//
//   manic examples/matrix_addition.manic
//   manic examples/matrix_addition.manic --record out --fps 60

title("Matrix Addition");
canvas(1280, 720);

text(head, (640, 120), "add two matrices, entry by entry");
display(head);  color(head, cyan);  size(head, 28);  hidden(head);
text(cap, (640, 600), "");  color(cap, dim);  size(cap, 26);

//        A            +            B            =            C
matrix(A, "2 1; 0 3", (280, 350), 74, 66);
matrix(B, "1 4; 5 2", (640, 350), 74, 66);
matrix(C, "3 5; 5 5", (1000, 350), 74, 66);

text(plus, (460, 350), "+");  display(plus);  color(plus, magenta);  size(plus, 44);  hidden(plus);
text(eq, (820, 350), "=");    display(eq);    color(eq, magenta);    size(eq, 44);    hidden(eq);

// A and B trace/fade in; C is built up during the sweep
untraced(A.lbrack);  untraced(A.rbrack);
untraced(B.lbrack);  untraced(B.rbrack);
untraced(C.lbrack);  untraced(C.rbrack);
hidden(A.entries);  hidden(B.entries);  hidden(C.entries);

// --- reveal the two matrices ---
show(head, 0.5);
say(cap, "two matrices, A and B");
par { draw(A.lbrack, 0.4);  draw(A.rbrack, 0.4);  draw(B.lbrack, 0.4);  draw(B.rbrack, 0.4); }
par { show(A.entries, 0.4);  show(B.entries, 0.4); }
show(plus, 0.3);
wait(0.5);

// --- add entry by entry ---
section("Entry by entry");
say(cap, "add matching entries, position by position");
par { show(eq, 0.3);  draw(C.lbrack, 0.4);  draw(C.rbrack, 0.4); }

seq {
  par { flash(A.r0c0, lime);  flash(B.r0c0, lime); }
  say(cap, "2 + 1 = 3");
  par { show(C.r0c0, 0.3);  pulse(C.r0c0); }

  par { flash(A.r0c1, lime);  flash(B.r0c1, lime); }
  say(cap, "1 + 4 = 5");
  par { show(C.r0c1, 0.3);  pulse(C.r0c1); }

  par { flash(A.r1c0, lime);  flash(B.r1c0, lime); }
  say(cap, "0 + 5 = 5");
  par { show(C.r1c0, 0.3);  pulse(C.r1c0); }

  par { flash(A.r1c1, lime);  flash(B.r1c1, lime); }
  say(cap, "3 + 2 = 5");
  par { show(C.r1c1, 0.3);  pulse(C.r1c1); }
}
wait(0.4);

// --- the result ---
section("Result");
say(cap, "A + B — every entry, all at once");
recolor(C.entries, cyan, 0.4);
par { pulse(C.r0c0);  pulse(C.r0c1);  pulse(C.r1c0);  pulse(C.r1c1); }
wait(1.5);

matrix_addition_plane

The same sum, laid out on a coordinate plane.

// Matrix Addition, Geometrically — a 2x1 matrix IS a vector. Adding two of them
//
//     [3]   [1]   [4]
//     [1] + [2] = [3]
//
// is the same as sliding one arrow onto the tip of the other (tip-to-tail) and
// reading off where you land. The column matrices at the top stay in lockstep
// with the arrows on the plane, so you see the algebra and the geometry at once.
//
//   manic examples/matrix_addition_plane.manic
//   manic examples/matrix_addition_plane.manic --record out --fps 60

title("Matrix Addition on the Plane");
canvas(1280, 720);

text(head, (640, 96), "a 2x1 matrix is a vector — adding them is tip-to-tail");
display(head);  color(head, cyan);  size(head, 26);  hidden(head);
text(cap, (640, 686), "");  color(cap, dim);  size(cap, 24);

// --- the equation, as column matrices across the top ---
matrix(MA, "3; 1", (452, 186), 44, 42);   color(MA, cyan);
text(plus, (528, 186), "+");   display(plus);   color(plus, dim);   size(plus, 40);   hidden(plus);
matrix(MB, "1; 2", (600, 186), 44, 42);   color(MB, magenta);
text(eq,  (676, 186), "=");    display(eq);     color(eq, dim);     size(eq, 40);     hidden(eq);
matrix(MC, "4; 3", (748, 186), 44, 42);   color(MC, lime);

// --- the plane, centred low so the arrows have room to climb ---
plane(pl, (640, 438), 560, 250, 48);

// vectors from the plane's origin (dy is up); unit = 48 px
vector(va, (640, 438), (144, 48),  cyan);      // a = (3, 1) -> tip (784, 390)
vector(vb, (640, 438), (48, 96),   magenta);   // b = (1, 2) -> tip (688, 342)
vector(vs, (640, 438), (192, 144), lime);      // a+b = (4, 3) -> tip (832, 294)

// the two translated copies that build the parallelogram
arrow(vb2, (784, 390), (832, 294));   color(vb2, magenta);   glow(vb2, 0);
arrow(va2, (688, 342), (832, 294));   color(va2, cyan);      glow(va2, 0);

// everything but the plane grid starts hidden / untraced
untraced(pl.x);  untraced(pl.y);  hidden(pl.grid);
untraced(va);  untraced(vb);  untraced(vs);  untraced(vb2);  untraced(va2);
untraced(MA.lbrack);  untraced(MA.rbrack);  hidden(MA);
untraced(MB.lbrack);  untraced(MB.rbrack);  hidden(MB);
untraced(MC.lbrack);  untraced(MC.rbrack);  hidden(MC);

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

// --- vector a ---
section("Vector a");
say(cap, "a = [3, 1] — three right, one up");
par { draw(MA.lbrack, 0.3);  draw(MA.rbrack, 0.3); }
par { show(MA, 0.3);  draw(va, 0.6); }
wait(0.4);

// --- vector b ---
section("Vector b");
say(cap, "b = [1, 2] — one right, two up");
show(plus, 0.3);
par { draw(MB.lbrack, 0.3);  draw(MB.rbrack, 0.3); }
par { show(MB, 0.3);  draw(vb, 0.6); }
wait(0.4);

// --- tip to tail ---
section("Tip to tail");
say(cap, "slide b so its tail sits on the tip of a");
draw(vb2, 0.7);
wait(0.5);

// --- the sum ---
section("The sum");
say(cap, "the arrow to that new point is a + b = [4, 3]");
show(eq, 0.3);
par { draw(MC.lbrack, 0.3);  draw(MC.rbrack, 0.3); }
par { show(MC, 0.3);  draw(vs, 0.8); }
par { pulse(vs);  flash(MC, lime); }
wait(0.6);

// --- parallelogram ---
section("Either order");
say(cap, "slide a onto b instead — same point. a + b = b + a");
draw(va2, 0.7);
wait(0.4);
say(cap, "the two paths frame a parallelogram; a + b is its diagonal");
par { pulse(va);  pulse(vb);  pulse(vs); }
wait(1.4);

linear_transform

A 2x2 matrix shearing a grid + basis vectors.

// Linear Transformation — a 2x2 matrix bends the whole plane. The grid, the
// basis vectors i-hat / j-hat, and a sample point all carry the tag `pl`, so a
// single `transform` applies the matrix to everything at once (Manim's
// ApplyMatrix). Straight lines stay straight; the grid shears / rotates.
//
//   manic examples/linear_transform.manic
//   manic examples/linear_transform.manic --template blueprint

title("Linear Transformation");
canvas("16:9");

let ox = cx;   let oy = cy;

text(head, (cx, 84), "a matrix bends the whole plane -- watch the grid");
display(head);  color(head, cyan);  size(head, 24);  hidden(head);
text(cap, (cx, 672), "");  color(cap, dim);  size(cap, 23);

// the plane (its grid + axes are all tagged `pl`)
plane(pl, (ox, oy), 580, 320, 60);
// basis vectors + a sample point, all tagged `pl` so they transform together
vector(vi, (ox, oy), (120, 0), cyan);      stroke(vi, 4);   tag(vi, pl);
vector(vj, (ox, oy), (0, -120), magenta);  stroke(vj, 4);   tag(vj, pl);
dot(mark, (ox + 180, oy - 100), 9);  color(mark, lime);  glow(mark, 1.6);  tag(mark, pl);

// --- script ---
show(head, 0.5);
say(cap, "the identity grid, with i-hat (cyan) and j-hat (magenta)");
wait(0.7);

section("Shear");
say(cap, "shear: i-hat stays put, j-hat leans over");
transform(pl, (ox, oy), 1, 0.5, 0, 1, 1.4, smooth);
wait(0.8);

section("Undo");
say(cap, "the inverse matrix brings it right back");
transform(pl, (ox, oy), 1, -0.5, 0, 1, 1.4, smooth);
wait(0.6);

section("Rotate");
say(cap, "a rotation matrix turns the whole plane");
transform(pl, (ox, oy), 0.707, -0.707, 0.707, 0.707, 1.5, smooth);
wait(1.3);

table

A ruled table; cells, rows, columns, labels all addressable.

// Tables — a ruled grid of entries with row/column headers, manic's Table /
// MathTable / IntegerTable. This is an addition table: each body cell is
// row + column. We reveal it, then "look up" 2 + 5 by flashing that row and
// column and lighting the answer — a demo of the table's tag addressing
// (row{i} / col{j} / the labels / the grid lines are all recolourable).
//
//   manic examples/table.manic
//   manic examples/table.manic --record out --fps 60

title("Tables");
canvas(1280, 720);

text(head, (640, 110), "a grid you can read by row and column");
display(head);  color(head, cyan);  size(head, 28);  hidden(head);
text(cap, (640, 620), "");  color(cap, dim);  size(cap, 24);

// body cells are the sums; headers are the addends (top-left corner is blank)
table(t, "0 5 10; 2 7 12; 4 9 14", (640, 372), 120, 78, "0 5 10", "0 2 4");
untraced(t.lines);
hidden(t.labels);  hidden(t.entries);

// --- reveal ---
show(head, 0.5);
say(cap, "rule the grid, then fill it in");
draw(t.lines, 1.0);
show(t.labels, 0.5);
show(t.entries, 0.5);
wait(0.5);

// --- a lookup: 2 + 5 = 7 ---
section("Look it up");
say(cap, "read a cell as row + column");
par { flash(t.rowlabel1, lime);  flash(t.collabel1, lime); }
say(cap, "row 2, column 5 ...");
par { flash(t.row1, cyan);  flash(t.col1, cyan); }
say(cap, "2 + 5 = 7");
recolor(t.r1c1, lime, 0.3);
pulse(t.r1c1);
wait(1.4);

table_braces

A table annotated with braces.

// Table + Braces — a quarterly sales table, annotated with curly braces that
// group its columns (the four quarters into two halves of the year) and its
// rows (the two regions). A practical pattern: use a table for the data and
// braces to call out how its rows/columns cluster.
//
// The brace coordinates are aligned to the table's grid lines by hand — the
// table is centred at (640,360) with 110x70 cells, so its vertical rules fall
// at x = 365 + k*110 and its rows span y = 325..465.
//
//   manic examples/table_braces.manic
//   manic examples/table_braces.manic --record out --fps 60

title("Sales by Region");
canvas(1280, 720);

text(head, (640, 96), "a data table, with its groups braced");
display(head);  color(head, cyan);  size(head, 26);  hidden(head);
text(cap, (640, 640), "");  color(cap, dim);  size(cap, 24);

// rows = regions, columns = quarters; body cells are sales (in $k)
table(t, "12 15 18 20; 9 11 14 16", (640, 360), 110, 70, "Q1 Q2 Q3 Q4", "North South");
untraced(t.lines);
hidden(t.labels);  hidden(t.entries);

// column braces over the header row (bulge up: points run right -> left)
bracelabel(h1, (695, 248), (475, 248), "H1", 26);   color(h1, magenta);   hidden(h1);
bracelabel(h2, (915, 248), (695, 248), "H2", 26);    color(h2, lime);      hidden(h2);

// a vertical brace to the left of the row labels, grouping the two regions
bracelabel(reg, (356, 325), (356, 465), "Regions", 26);   color(reg, cyan);   hidden(reg);

// --- reveal the table ---
show(head, 0.5);
say(cap, "quarterly sales for two regions");
draw(t.lines, 1.0);
show(t.labels, 0.5);
show(t.entries, 0.5);
wait(0.4);

// --- brace the columns into halves of the year ---
section("Halves of the year");
say(cap, "Q1-Q2 are the first half, Q3-Q4 the second");
par { flash(t.col0, magenta);  flash(t.col1, magenta); }
show(h1, 0.5);
par { flash(t.col2, lime);  flash(t.col3, lime); }
show(h2, 0.5);
wait(0.4);

// --- brace the rows into regions ---
section("The regions");
say(cap, "and the two rows are the regions");
par { flash(t.rowlabel0, cyan);  flash(t.rowlabel1, cyan); }
show(reg, 0.6);
wait(1.4);