Core Idea
A vector is an arrow: a direction and a length, and nothing else. The three numbers are only the arrow’s shadow on some coordinate frame — the same arrow casts different numbers in different frames, which is why a bare (3, 2, 0) means nothing until you know whose axes it’s measured in (coordinate spaces makes that point touchable).
The swarm above is vectors at work: every agent carries its velocity as an arrow, and the gold wind vector is added to all ninety of them at once — drag it and feel a single addition steer the whole field. That is most of gameplay, physics, and animation programming: arrows added to arrows, every frame.
A Point Is Not a Vector
A point is a somewhere; a vector is a which-way-and-how-far. They trade through one rule — end minus start:
b − a = the arrow FROM a TO b |b − a| = the distance between them
Get the order backwards and everything downstream points the wrong way: lightPos − surfacePoint aims at the light, surfacePoint − lightPos aims away from it. (The pipeline encodes the point/vector distinction as w = 1 vs w = 0 — homogeneous coordinates tells that story.)
Arrow Arithmetic
Addition is tip-to-tail: carry b so it starts where a ends. In the demo both dashed routes — b after a, and a after b — land on the same corner of the parallelogram: a + b = b + a, which is why forces and velocities can be accumulated in any order. Subtraction is that gold b − a arrow between the tips — and it is not symmetric: a − b is the same arrow reversed.
Length, and Why It Must Be 1
Magnitude is Pythagoras run through every component:
|v| = √(x² + y² + z²)
Normalizing — v / |v| — slides the arrow onto the unit circle (press â = a/|a| in the demo): direction kept, length surrendered. Unit vectors matter because most formulas silently assume them; the dot product only reads as an angle when both arrows have length 1. It’s also an everyday movement bug: pressing forward and right together gives an input of length √2 — diagonal motion 41% too fast — until you normalize and rescale:
velocity = input.normalize().multiplyScalar(speed) // same speed in every direction
The wind demo’s clamp toggle is the same move at scale: normalize(v) · max — every arrow keeps its direction, no arrow may exceed the cap. One caution: the zero vector has no direction, so normalizing it divides by zero — guard with a length check.
Three Ways to Multiply
| Operation | Output | What it’s for |
|---|---|---|
component-wise (aₓbₓ, a_yb_y, a_zb_z) |
vector | non-uniform scaling |
dot product a·b |
scalar | angles, projection, lighting |
cross product a×b |
vector | perpendiculars, normals, winding |
Knowing which multiplication a problem wants is half of graphics debugging.
Connections
Vectors are the raw material everything else here refines: the dot product measures them against each other, the cross product builds normals out of them, and a matrix is nothing but the arrows its columns say the basis becomes. Their numbers only mean something inside a frame — the founding idea of coordinate spaces — and the pipeline’s point/direction split is homogeneous coordinates giving the arrow itself a w.