# A matrix library in 5 lines of code

Here’s an animation of a pulsating rectangle, spinning around the origin. It’s nothing special, except that it’s drawn with the power of ✨matrix math✨, using my own matrix library (5 lines of code!).

For years, on and off, I tried to internalize matrix operations, but it never stuck. I remember watching a Khan Academy linear algebra course around ten years ago, where Sal’s guidance was basically, “A matrix is a grid of numbers. Here is the matrix multiplication number-crunching algorithm.”

Sal Khan is a *great* teacher, but this approach was wrong.
Recently, I watched Grant Sanderson’s *Essence of Linear Algebra* course,
which made things not just clear, but almost ... obvious!
Here is the fundamental insight:
a matrix describes a linear function by recording where the basis vectors move to.
This insight is so important that I’ll say it again:

A matrix describes a linear function by recording where the basis vectors move to.

Everything else follows smoothly from this definition of a matrix.
Armed with just this insight,
I no longer have to *remember* how to “multiply matrices”.
Instead, I can just work it out from the definition
(which is that matrix “multiplication” is actually *function composition*.)
Here is my tiny matrix math library:

```
const zipWith = (f, a, b) => a.map((k, i) => f(k, b[i])); // helper
// Vector ops
const vecScale = (n, v) => v.map(c => n*c);
const vecAdd = (v1, v2) => zipWith((c1,c2)=>c1+c2, v1, v2);
// Matrix ops
const matApply = (mat, vec) => zipWith(vecScale, vec, mat).reduce(vecAdd);
const matMul = (m2, m1) => m1.map(v => matApply(m2,v));
```

In this system, matrices are written in “column-major format”: a list of basis vectors. Here are some examples for 2D matrix math:

```
const identity = [
[1, 0], // the x basis vector ("i-hat"). It hasn't moved anywhere.
[0, 1], // the y basis vector ("j-hat"). It hasn't moved anywhere.
];
const rotateClockwise90 = [
[-1, 0], // the rotated x basis vector ("i-hat")
[ 1, 0], // the rotated y basis vector ("j-hat")
];
// Common matrix constructors
const rotate = a => [[Math.cos(a), Math.sin(a)], [-Math.sin(a), Math.cos(a)]];
const scaleSep = (s) => [ [s[0], 0], [0, s[1]] ];
const scale = s => scaleSep([s,s]);
```

For my purposes (graphics programming),
linear functions have at least two big deficiencies.
Linear functions can’t describe *translation* (that is, moving stuff!),
because they preserve the origin point.
And linear functions can’t describe *projection*
(that is, simulating a camera),
because they keep parallel lines parallel.

In my next post, I describe *homogeneous coordinates*,
a mathematical hack that builds on top of plain linear algebra,
and allows describing translation and projection.
Stay tuned!

This page copyright James Fisher 2020. Content is not associated with my employer.