In which you learn how to spin an object around the origin.

Rotation is more complex than scaling and translating, especially when you are rotating in three dimensions. For the time being, you will restrict yourself to 2D rotations.

The first step in inventing the mathematics behind rotation is to recognize that a point \(p\) can be expressed either in Cartesian coordinates or in polar coordinates as a radius and angle:

$$ \begin{align*} p &= \begin{bmatrix}x & y\end{bmatrix} &\leftarrow \text{Cartesian}\\ &= (r, a) &\leftarrow \text{polar} \end{align*} $$

The following sequence illustrates how Cartesian coordinates and polar coordinates relate:

Trigonometry tells us that these the Cartesian coordinates and polar coordinates have this relationship:

$$ \begin{aligned} x &= r \cdot \cos a \\ y &= r \cdot \sin a \end{aligned} $$

You don't have do anything with these equations yet. Just remember that if you have a point's polar coordinates, you can turn them into Cartesian coordinates with sine and cosine.

Here then is the problem of rotation. You have an existing point \(p\), and you want to rotate it \(b\) radians to compute the rotated point \(p'\). In polar coordinates, \(p'\) is a lot like \(p\); it's just rotated a bit more:

$$ \begin{aligned} p &= (r, a) \\ p' &= (r, a + b) \\ \end{aligned} $$

Now you apply your polar-to-Cartesian formulas from above to find the rotated point's Cartesian coordinates:

$$ \begin{aligned} x' &= r \cdot \cos (a + b) \\ y' &= r \cdot \sin (a + b) \\ \end{aligned} $$

Those cosine and sine of sums may be rewritten thanks to some handy trigonometric identities that you may not remember. These are the sum identities:

$$ \begin{aligned} \cos(a + b) &= \cos a \cdot \cos b - \sin a \cdot \sin b \\ \sin(a + b) &= \sin a \cdot \cos b + \cos a \cdot \sin b \end{aligned} $$

After applying the identities, you have these seemingly unhelpful equations:

$$ \begin{aligned} x' &= r \cdot (\cos a \cdot \cos b - \sin a \cdot \sin b) \\ y' &= r \cdot (\sin a \cdot \sin b + \cos a \cdot \cos b) \\ \end{aligned} $$

But wait. You can simplify a bit. First you distribute \(r\):

$$ \begin{aligned} x' &= r \cdot \cos a \cdot \cos b - r \cdot \sin a \cdot \sin b \\ y' &= r \cdot \sin a \cdot \sin b + r \cdot \cos a \cdot \cos b \\ \end{aligned} $$

Do you see that \(r \cdot \cos a\)? You know what that is? That's \(x\). You saw it earlier. And \(r \cdot \sin a\) is \(y\). You substitute these in:

$$ \begin{aligned} x' &= x \cdot \cos b - y \cdot \sin b \\ y' &= x \cdot \sin b + y \cdot \cos b \end{aligned} $$

And those are your equations for rotating. You combine the x- and y-coordinates of the original position together with some trigonometric functions.

In GLSL, you can give \(b\) a more meaningful name, like radians. The equations then translate into this vertex shader:

uniform float radians;
in vec3 position;

void main() {
  gl_Position = vec4(
    position.x * cos(radians) - position.y * sin(radians),
    position.x * sin(radians) + position.y * cos(radians),

On the CPU side, we supply a value for radians, which is a scalar:

shaderProgram.setUniform1f('radians', 0.1);

Try adding rotation to one of your renderers.