# 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!

With Vidrio

With generic competitor

### More by Jim

- Your syntax highlighter is wrong
- Granddad died today
- The Three Ts of Time, Thought and Typing: measuring cost on the web
- I hate telephones
- The sorry state of OpenSSL usability
- The dots do matter: how to scam a Gmail user
- My parents are Flat-Earthers
- How Hacker News stays interesting
- Project C-43: the lost origins of asymmetric crypto
- The hacker hype cycle
- The inception bar: a new phishing method
- Time is running out to catch COVID-19
- A probabilistic pub quiz for nerds
- Smear phishing: a new Android vulnerability

Tagged #mathematics, #programming, #js, #graphics. All content copyright James Fisher 2020. This post is not associated with my employer. Found an error? Edit this page.