# Why are rotations in 2D game engines often counter-clockwise = positive systems?

I was looking over the API for a game engine a friend was developing in her spare time. She made the decision to make positive angles rotate in a clockwise direction. This immediately struck me as odd. My intuition was that angle should be measured counter-clockwise. I envision the typical unit circle when thinking about 2D rotations:

Her reasoning for picking this convention was that she was using a "positive y" = "down" coordinate system. From a mathematical sense, it makes sense to me that rotations would be clockwise if you're using this orientation. sin(90 degrees) would give me y = +1, which would be in a downward direction on my screen. So shouldn't I be thinking about angles in a clockwise way?

Why have we arrived at the convention that rotations should be counter-clockwise then, even in engines where positive y is down?

• "This immediately struck me as odd. My intuition was that angle should be measured counter-clockwise" — that may itself be your answer, if you consider that many developers might have the same first reaction as you. ;) – DMGregory Jan 23 '20 at 17:24
• Can you give examples of two game engines where positive is counterclockwise? – Tanner Swett Jan 30 '20 at 23:29

Why have we arrived at the convention that rotations should be counter-clockwise then, even in engines where positive y is down?

Have we? Let us try CSS:

const box = document.getElementById("box");

function step(timestamp)
{
let deg = timestamp/10;
box.style.transform = "rotate(" + deg + "deg)";
window.requestAnimationFrame(step);
}

window.requestAnimationFrame(step);
#box
{
width: 100px;
height: 100px;
border: 1px solid black;
}
<div id="box"></div>

Hmm... It look clockwise to me.

Let us try with canvas:

const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");

canvas.width = 250;
canvas.height = 250;

const cx = canvas.width/2;
const cy = canvas.height/2;

const x = -50;
const y = -50;
const w = 100;
const h = 100;

function step(timestamp)
{
let deg = timestamp/10;
let rad = deg * Math.PI/180;

// erase
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.restore();

// draw
ctx.save();
ctx.translate(cx, cy); // pivot point
ctx.fillRect(x, y, w, h);
ctx.restore();

window.requestAnimationFrame(step);
}

window.requestAnimationFrame(step);
<canvas id="canvas"></canvas>

Still clockwise.

WebGL?

const canvas = document.getElementById("canvas");
const gl = canvas.getContext("webgl");

const vsSource = 
attribute vec4 aVertexPosition;

uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;

void main() {
gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
}
;

const fsSource = 
void main() {
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
;

}

const shaderProgram = gl.createProgram();

const programInfo = {
attribLocations: {
},
uniformLocations: {
},
};

// ---

const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

const positions = [
-1.0,  1.0,
1.0,  1.0,
-1.0, -1.0,
1.0, -1.0,
];
const numComponents = 2;
const componentType = gl.FLOAT;
const vertexCount = 4;

gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

// ---

gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearDepth(1.0);

// ---

function step(timestamp)
{
let deg = timestamp/10;
let rad = deg * Math.PI/180;

//erase

gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

//draw

gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(
programInfo.attribLocations.vertexPosition,
numComponents,
componentType,
false,
0,
0);
gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition);
gl.useProgram(programInfo.program);
gl.uniformMatrix4fv(
programInfo.uniformLocations.projectionMatrix,
false,
[1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0]);
gl.uniformMatrix4fv(
programInfo.uniformLocations.modelViewMatrix,
false,
0.0          , 0.0           , 1.0, 0.0,
0.0          , 0.0           , 0.0, 1.0]);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertexCount);

window.requestAnimationFrame(step);
}

window.requestAnimationFrame(step);
#canvas
{
width: 250px;
height: 250px;
}
<canvas id="canvas"></canvas>

Clockwise too. And I believe I have wrote that rotation matrix as per math convention. You can compare with Rotation Matrix at Wolfram MathWorld or Rotation Matrix at Wikipedia.

I would remind you that web technologies are standard.

So, if your friend is doing the rotation like this, then it is correct. And you are right, this makes sense with a vertical axis going down. We think of angles going counter-clockwise because we think of the vertical axis going up, which is mathematical convention. However, the vertical axis in screen goes down, because historical reasons.

I could have, of course, flipped the vertical axis in the projection matrix. Then the rotation would be counter-clockwise. In fact, it is more or less common that developers do some axis flipping to make it match their expectations.

• I feel this is a little bit like the Coriolis effect. Does Water Swirl the Other Way in the Southern Hemisphere?. The Coriolis Effect Test: two hemispheres, one sink. Do object actually rotate the other way in that game engine? – Theraot Jan 28 '20 at 9:06
• In the WebGL one you are setting your own rotation matrix, so it can be whichever way you like. – user253751 Jan 30 '20 at 16:54
• I get why CSS and Canvas are following the 2D convention of Y+ = down, but does WebGL actually not use the same default coordinate system as OpenGL? On OpenGL the default coordinate system with a "default" viewport is Y+ = up. – Romen Jan 30 '20 at 22:25

Positive angles of rotation follow the “Right Hand Rule.” Place your right hand with palm at origin and fingers along positive x-axis. Curl your fingers toward the positive y-axis. The direction your fingers curl is a positive angle by convention.

• Ok, but... Why are rotations in 2D game engines often counter-clockwise = positive systems? – Vaillancourt Jan 30 '20 at 12:58
• This is neither right-handed nor left-handed: I can perform the gesture you describe with either hand. To have a handedness, we need to specify the direction your thumb is pointing: into the screen or out of the screen. So 3D coordinate systems with an x, y, and Z axis come in left-handed and right-handed versions, following the left-hand and right-hand rule respectively, but this does not directly apply to 2D coordinate systems. – DMGregory Jan 30 '20 at 14:12