In the previous chapter, we gave each pixel an address. Now let’s put two points on that grid and draw an arrow from one to the other.
Moving a point changes the arrow. Moving both points together moves the whole arrow, but keeps its direction and size. We want to describe these two operations with code.
We will keep the same coordinates: zero at the center, positive Y upward, and equal units on both axes. If those choices are unfamiliar, start with chapter 01.
One type, two meanings
Look at this pair of values:
vec2 value = vec2(0.4, 0.2);
It can describe a point: a position 0.4 units right of the origin and 0.2 units above it. Or it can describe a displacement: move 0.4 units right and 0.2 units up from wherever you start.
The numbers are the same. Their meaning is different. A point answers “where?” A displacement answers “how much does the position change?”
GLSL stores both in vec2. It does not know which meaning we intend. Names such as position, center, and displacement help us keep track of it.
A displacement is a vector. We draw it as an arrow to show its direction and size, but the vector itself does not require a particular starting point. The same displacement can start at A, at the origin, or somewhere else.
Find the movement from A to B
Our starting point is A. Our destination is B:
vec2 a = vec2(-0.6, -0.3);
vec2 b = vec2( 0.6, 0.4);
To find the required movement, subtract the starting position from the destination:
vec2 displacement = b - a;
Subtraction works component by component:
// X: 0.6 - (-0.6) = 1.2
// Y: 0.4 - (-0.3) = 0.7
vec2 displacement = vec2(1.2, 0.7);
So we need to move 1.2 units right and 0.7 units up. The horizontal and vertical guides in the playground show these two changes separately. The diagonal arrow shows their combined result.
Those guides help us read the components. They do not mean that an object must first move horizontally and then vertically. The vector describes the total change, not a required path or speed.
Order matters. For the movement from B to A, we swap the subtraction:
vec2 back = a - b; // (-1.2, -0.7)
// Also: back = -displacement;
Both signs change. Right becomes left; up becomes down. The arrow now points in the opposite direction.
Build the endpoint
Once we have a displacement, add it to a starting point:
vec2 end = a + displacement;
With our values, the result is (-0.6 + 1.2, -0.3 + 0.7), which gives (0.6, 0.4). We have arrived at B.
This gives us two useful operations:
Point − point = displacement
Point + displacement = point
Now start the same movement at (0, 0):
vec2 otherStart = vec2(0.0);
vec2 otherEnd = otherStart + displacement; // (1.2, 0.7)
The arrow has moved, but the displacement is still (1.2, 0.7). Its components belong to the movement, not to its location.
A common mistake is to write a + b when we want to reach B from A. GLSL accepts it, but it adds the coordinates of two positions. In this example it gives (0.0, 0.1), which is neither B nor the displacement. Decide what the result should mean before choosing the operation.
Make the movement smaller — or reverse it
Multiply a vector by a number to scale both components by the same amount:
vec2 halfStep = displacement * 0.5; // (0.6, 0.35)
vec2 tip = a + halfStep; // (0.0, 0.05)
We made half of each change, so the new endpoint is halfway from A to B. More generally:
vec2 tip = a + k * (b - a);
k is a scalar: one number, unlike the two components in our vector. It scales the movement from A. It does not scale A itself.
| k | Scaled displacement | Endpoint from A |
|---|---|---|
| 0 | (0, 0) | (-0.6, -0.3) — A |
| 0.5 | (0.6, 0.35) | (0, 0.05) — halfway |
| 1 | (1.2, 0.7) | (0.6, 0.4) — B |
| 2 | (2.4, 1.4) | (1.8, 1.1) — beyond B |
| -1 | (-1.2, -0.7) | (-1.8, -1.0) — opposite direction |
A positive value keeps the direction. A negative value reverses it. Zero gives the zero vector, vec2(0.0): no movement, with no direction to draw.
There is an important difference between a + k * displacement and k * (a + displacement). The first keeps the start at A. The second also scales A, so it scales the final position relative to the origin. Try k = 0: the first stays at A; the second moves to zero.
Move both points together
Let’s move the whole arrow by an offset (0.3, -0.2): right by 0.3 and down by 0.2.
vec2 offset = vec2(0.3, -0.2);
vec2 movedA = a + offset; // (-0.3, -0.5)
vec2 movedB = b + offset; // ( 0.9, 0.2)
vec2 sameDisplacement = movedB - movedA; // (1.2, 0.7)
The new positions are different, but their difference is unchanged. We added the same offset to both, and subtraction cancels it:
// (b + offset) - (a + offset) = b - a
This is a translation: every point receives the same displacement. It moves the arrow without changing its direction or size.
Try the common-translation controls below. A and B should move together while the readout for B − A stays fixed. If that readout changes, the points did not receive the same offset.