Linear Transformations
Plenty of functions "move points around" — stretch a photo, warp a map, bend a curve. But not every
one of them can ever be written as a matrix. Before the whole matrix toolkit (composing
transformations, inverting them, finding eigenvalues) can be pointed at a transformation, that
transformation has to earn the right by passing a strict gatekeeper test.
Stop thinking of a matrix as a table of numbers, and start thinking of it as an
action: a rule that takes in a vector and moves it to a new one, transforming the
entire plane at once. A linear transformation is a special, very well-behaved kind
of motion. It must keep two promises, no exceptions:
- the origin stays fixed — T(\vec 0) = \vec 0, and
- every grid line stays straight, parallel and evenly spaced.
In symbols, a transformation T is linear exactly when it respects both
addition and scaling for every pair of vectors \vec u, \vec v
and every scalar c:
T(\vec{u}+\vec{v}) = T(\vec{u}) + T(\vec{v}) \qquad \text{and} \qquad T(c\vec{v}) = c\,T(\vec{v}).
No bending, no curving, no shifting off the origin — just a clean stretch-and-skew of space. The
rest of this page is about learning to check those two promises, rather than trusting a
transformation just because it looks tidy.
The two promises, one at a time
Additivity — T(\vec u + \vec v) = T(\vec u) + T(\vec v)
— says it doesn't matter whether you add two vectors first and then transform the sum, or
transform each one separately and add the results after: you land in the same place either
way. Picture the parallelogram with \vec u and
\vec v as two edges — its far corner is \vec u+\vec
v. Additivity says applying T to the whole parallelogram
(edges and all) just gives another parallelogram, built the same way from
T(\vec u) and T(\vec v).
Scaling (also called homogeneity) —
T(c\vec v) = c\,T(\vec v) — says stretching a vector by a factor
c before transforming it gives the same answer as transforming it first
and then stretching the result by c. Geometrically: a whole line of
evenly spaced points through the origin (multiples of \vec v) lands on
another line of evenly spaced points through the origin — never bunched up, never spread apart
unevenly, never bent into a curve.
Put the two together and you get the picture from the definition: a grid of straight, parallel,
evenly spaced lines through the origin must land as another grid of straight, parallel, evenly
spaced lines through the origin. That rigidity is the whole reason a single matrix — just a handful
of numbers — can capture the entire transformation.
Watch space deform
Drag the slider to apply a transformation gradually. The grid stretches and shears — yet every line
stays a line, parallel lines stay parallel, and the centre never moves. Watch the two coloured
arrows: they track where the "unit" directions (1,0) and
(0,1) end up, and once you know that, additivity and scaling pin down
everywhere else — you're watching the two defining properties in action, not just a pretty
animation.
Worked example: checking a scaling map
Let T(x,y) = (2x, 2y) — every point moves twice as far from the origin.
Is it linear? Test it on two arbitrary vectors
\vec u = (u_1,u_2) and \vec v=(v_1,v_2),
rather than trusting a guess.
Additivity:
T(\vec u+\vec v) = T(u_1+v_1,\ u_2+v_2) = \big(2(u_1+v_1),\ 2(u_2+v_2)\big) = (2u_1+2v_1,\ 2u_2+2v_2).
T(\vec u)+T(\vec v) = (2u_1,2u_2) + (2v_1,2v_2) = (2u_1+2v_1,\ 2u_2+2v_2).
The two sides match exactly, for any choice of \vec u,\vec v — not just a lucky pair of numbers.
Scaling: T(c\vec v) = (2cv_1, 2cv_2) = c(2v_1,2v_2) = c\,T(\vec v). Both promises hold, so doubling is linear.
A concrete check with numbers: take \vec u=(1,3) and
\vec v=(4,-1). Then \vec u+\vec v=(5,2), so
T(\vec u+\vec v) = (10,4). Separately,
T(\vec u)=(2,6) and T(\vec v)=(8,-2), and their
sum is (10,4) too — matching, as the general argument promised.
Worked example: checking a 90° rotation
Rotating every point 90° counter-clockwise around the origin sends (x,y)
to (-y,x). Test it on \vec u=(1,0) and
\vec v=(0,1), then on their sum.
T(\vec u)=(0,1), \qquad T(\vec v)=(-1,0), \qquad T(\vec u)+T(\vec v)=(-1,1).
\vec u+\vec v=(1,1), \qquad T(\vec u+\vec v)=(-1,1).
They agree. Rotation also preserves scaling: shrinking \vec v to
\tfrac12\vec v=(0,0.5) and rotating gives
(-0.5,0), exactly \tfrac12 of
T(\vec v)=(-1,0). A rotation never bends a line and it always fixes the
centre it spins around — so it was never in doubt, but now it's proven, not assumed.
Worked example: a transformation that fails — translation
Now test a transformation that looks perfectly innocent: sliding every point 3 units right and 2
units up, T(x,y) = (x+3,\ y+2). Before checking either property, apply
the fastest possible test — does the origin stay fixed?
T(0,0) = (0+3,\ 0+2) = (3,2) \neq (0,0).
That single computation is already fatal: a linear map is required to send the zero vector
to the zero vector (put c=0 into the scaling rule:
T(\vec 0)=T(0\cdot\vec v)=0\cdot T(\vec v)=\vec 0). Translation fails
before we even reach additivity. For the record, additivity fails too — take
\vec u=(1,0) and \vec v=(0,1):
T(\vec u+\vec v) = T(1,1) = (4,3), \quad\text{but}\quad T(\vec u)+T(\vec v) = (4,2)+(3,3) = (7,5).
(4,3) \neq (7,5). Sliding the whole plane sideways is a perfectly
reasonable thing to want to do to a picture — it's just not a linear thing to do. (It has
its own name, an affine transformation, and its own page.)
Worked example: a transformation that fails both properties badly
Squaring each coordinate, T(x,y) = (x^2, y^2), at least fixes the origin
— T(0,0)=(0,0) — so the fast test alone can't catch it. Check
additivity directly with \vec u=(1,0) and
\vec v=(1,0), so \vec u+\vec v = (2,0):
T(\vec u+\vec v) = T(2,0) = (4,0), \qquad T(\vec u)+T(\vec v) = (1,0)+(1,0) = (2,0).
4 \neq 2 — additivity is broken. Scaling fails too: with
c=2, T(2\vec u) = T(2,0) = (4,0), but
2\,T(\vec u) = 2(1,0) = (2,0). Squaring bends every straight line
(except the axes) into a curve, so it was always going to fail — but notice that fixing the origin
was not enough on its own to guarantee linearity. Both promises have to be checked, every
time.
// A tiny "linearity checker": tests additivity + scaling on random vectors.
function isLinear(T: (v: [number, number]) => [number, number]): boolean {
const u: [number, number] = [1.7, -2.3];
const w: [number, number] = [-0.4, 3.1];
const c = 2.5;
const sum: [number, number] = [u[0] + w[0], u[1] + w[1]];
const [Tu0, Tu1] = T(u);
const [Tw0, Tw1] = T(w);
const [Tsum0, Tsum1] = T(sum);
const additive = close(Tsum0, Tu0 + Tw0) && close(Tsum1, Tu1 + Tw1);
const scaled: [number, number] = [c * u[0], c * u[1]];
const [Tscaled0, Tscaled1] = T(scaled);
const homogeneous = close(Tscaled0, c * Tu0) && close(Tscaled1, c * Tu1);
return additive && homogeneous;
}
function close(a: number, b: number): boolean {
return Math.abs(a - b) < 1e-9;
}
console.log("doubling: ", isLinear(([x, y]) => [2 * x, 2 * y]));
console.log("rotate 90: ", isLinear(([x, y]) => [-y, x]));
console.log("translate: ", isLinear(([x, y]) => [x + 3, y + 2]));
console.log("square each: ", isLinear(([x, y]) => [x * x, y * y]));
What stays linear, what doesn't
Rotations, stretches, reflections and shears are all linear — every one of them passes both tests.
Bending a line into a curve is not linear; nor is sliding the whole plane sideways (that
moves the origin — it's an affine move). Because linear maps preserve the grid completely,
they're pinned down entirely by where just two arrows go, T(1,0) and
T(0,1) — the subject of the very next page.
-
Every linear transformation of \mathbb{R}^n can be represented by
some matrix.
-
Every matrix represents some linear transformation of the corresponding space.
-
"Linear transformation" and "matrix" are two names, two representations — words and a grid of
numbers — for exactly the same mathematical object. See
how
to build that matrix, column by column, from just the two arrows above.
A transformation can look deceptively simple and still fail. "Add 5 to every coordinate,"
"shift everything sideways," "always output (1,1) no matter what goes
in" — all sound like plausible candidates, and none of them are linear. The only honest way to know
is to run both tests, on general vectors, not just a single convenient example:
- Does T(\vec 0) = \vec 0? If not, stop — it's already disqualified.
- Does T(\vec u+\vec v) = T(\vec u)+T(\vec v) for arbitrary \vec u,\vec v?
- Does T(c\vec v) = c\,T(\vec v) for an arbitrary scalar c?
A single numerical example that happens to work is not a proof — it might just be
a coincidence for that pair of vectors. Checking on general symbols (like
(u_1,u_2) and (v_1,v_2), as in the worked
examples above) is what actually settles it.
If matrices can never translate, how does every 3-D game, CAD program and animation engine slide
objects around the screen constantly? With a clever bit of smuggling. Bolt one extra "fake"
coordinate onto every vector — turn (x,y) into
(x,y,1) — and suddenly a bigger matrix, acting linearly on the
padded 3-D vectors, can produce an effect that looks exactly like translation when you read off just
the first two coordinates. Translation sneaks in through the back door of a higher-dimensional
linear map. That trick is called
homogeneous
coordinates, and it's the reason graphics pipelines can treat rotation, scaling
and sliding as "just matrix multiplication" underneath it all.
A concrete taste: to slide every point 3 right and 2 up, pad each 2-D point
(x,y) with a third coordinate fixed at 1, then multiply by a bigger,
genuinely linear 3\times3 matrix:
\begin{bmatrix} 1 & 0 & 3 \\ 0 & 1 & 2 \\ 0 & 0 & 1 \end{bmatrix}\begin{bmatrix} x \\ y \\ 1 \end{bmatrix} = \begin{bmatrix} x+3 \\ y+2 \\ 1 \end{bmatrix}.
Read off just the first two numbers of the result and you've got exactly the translation that
failed the linearity test earlier on this page — except this time it's riding inside a matrix that
genuinely does fix its own (3-D) origin and keep its own grid lines straight. Nothing about the
rules of linearity was broken; the trick was to change which space you're secretly working in.
See it explained