Matrix Transformations in Games
What a Matrix Does
A transformation matrix is a compact encoding of an operation that moves, rotates, or resizes points in space. Instead of applying three separate operations to every vertex of a model (first scale, then rotate, then translate), you multiply the three transformation matrices together into a single combined matrix and apply that one matrix to each vertex. For a model with 1,000 vertices, this reduces 3,000 operations to 1,000 plus two matrix multiplications. The savings compound in a scene with dozens of models, each with thousands of vertices, processed 60 times per second.
A 2D transformation uses a 3x3 matrix. A 3D transformation uses a 4x4 matrix. The extra dimension (3x3 instead of 2x2, 4x4 instead of 3x3) enables translation through a technique called homogeneous coordinates. Without homogeneous coordinates, a 2x2 matrix can only rotate and scale, not translate. Adding the third row and column, and representing 2D points as (x, y, 1), allows translation to be encoded as matrix multiplication alongside rotation and scaling.
Matrix multiplication is not commutative: A * B does not generally equal B * A. This means the order of transformations matters. Scaling then rotating produces a different result than rotating then scaling. The standard convention is to apply transformations right to left: if you multiply S * R * T (scale times rotate times translate), the translation happens first, then rotation, then scaling. This convention matches how most rendering APIs and game engines process transforms.
The Three Fundamental Transforms
Translation moves an object by a fixed offset. A 2D translation matrix moves point (x, y) by (tx, ty). The matrix stores tx and ty in the third column (for column-major storage, used by OpenGL and WebGL) or the third row (for row-major storage). The identity matrix (ones on the diagonal, zeros elsewhere) represents no transformation, and modifying just the translation components produces a pure translation matrix. Translation is the most intuitive transform: it simply adds an offset to every point.
Rotation turns an object around an axis. In 2D, rotation is always around the z-axis (perpendicular to the screen), and the rotation matrix contains cos(theta) and sin(theta) terms arranged in a 2x2 block. In 3D, you can rotate around any axis. Rotation around the x-axis leaves x unchanged and mixes y and z. Rotation around the y-axis leaves y unchanged and mixes x and z. Rotation around the z-axis leaves z unchanged and mixes x and y. Arbitrary axis rotation (rotating around a vector that is not aligned with any coordinate axis) uses the Rodrigues rotation formula or quaternions.
Scaling resizes an object along each axis independently. A uniform scale (same factor on all axes) makes the object larger or smaller while preserving proportions. A non-uniform scale (different factors on different axes) stretches or squishes the object. The scaling matrix places the scale factors on the diagonal. A scale of (2, 1, 0.5) doubles the object's width, keeps the height unchanged, and halves the depth. Negative scale factors mirror the object across the corresponding axis, which is useful for flipping sprites horizontally or vertically.
Combining Transforms
The power of matrices is composition. To place a 3D model in a scene, you typically need to scale it to the correct size, rotate it to the correct orientation, and translate it to the correct position. Multiplying the scale, rotation, and translation matrices together produces a single 4x4 matrix called the model matrix (or world matrix). Applying this one matrix to every vertex of the model correctly scales, rotates, and positions every point in a single operation per vertex.
The standard multiplication order for a model matrix is translation * rotation * scale (T * R * S). This applies scaling first (so the object scales around its own origin), then rotation (so it rotates around its own origin at its own scale), then translation (so it moves to its world position). Reversing the order would cause the object to translate first, then rotate around the world origin rather than its own center, which is rarely the desired behavior.
Hierarchical transforms (parent-child relationships) multiply matrices down the chain. A character's arm position is the character's world matrix times the arm's local matrix. The hand's position is the character's world matrix times the arm's local matrix times the hand's local matrix. When the character moves, every child object moves with it automatically because the parent's translation propagates through the matrix multiplication. This is the scene graph model used by Three.js, Babylon.js, and virtually every 3D engine.
The Model-View-Projection Pipeline
Every 3D renderer transforms vertices through three matrix stages: model, view, and projection. Understanding this pipeline is essential for debugging visual issues and implementing custom rendering effects.
The model matrix transforms vertices from model space (local to the object, centered on its origin) to world space (the global coordinate system of the game). Each object in the scene has its own model matrix encoding its position, rotation, and scale in the world.
The view matrix transforms vertices from world space to view space (also called camera space or eye space), where the camera is at the origin looking down the negative z-axis. The view matrix is the inverse of the camera's own model matrix. If the camera is at position (10, 5, 20) looking toward the origin, the view matrix translates everything by (-10, -5, -20) and rotates everything so the camera's forward direction aligns with the negative z-axis. The effect is that the camera stays fixed and the entire world moves and rotates around it.
The projection matrix transforms vertices from view space to clip space, mapping the 3D scene onto a 2D image. Perspective projection makes distant objects appear smaller, creating the illusion of depth. The projection matrix encodes the field of view angle, aspect ratio, and near/far clipping planes. Orthographic projection preserves sizes regardless of distance, which is used for UI rendering, 2D games rendered in a 3D engine, and technical visualizations.
The combined MVP (model-view-projection) matrix is the product of all three: projection * view * model. In a WebGL shader, you multiply each vertex position by the MVP matrix in the vertex shader to produce the final screen position. The math is identical whether you use Three.js, Babylon.js, raw WebGL, or any other renderer. The engine sets up the matrices; the shader applies them.
Matrices in Web Game Development
Canvas 2D provides a built-in transformation system through ctx.transform(a, b, c, d, e, f) and ctx.setTransform(a, b, c, d, e, f). The six parameters represent a 3x3 matrix with the bottom row fixed as (0, 0, 1). The a and d parameters control scaling, b and c control rotation/skewing, and e and f control translation. Convenience methods ctx.translate(), ctx.rotate(), and ctx.scale() modify the current transform by multiplying in additional matrices. Use ctx.save() and ctx.restore() to push and pop transform states when drawing hierarchical objects.
WebGL requires you to manage matrices explicitly. You create transformation matrices in JavaScript, upload them to the GPU as uniform variables, and multiply them by vertex positions in your vertex shader. The gl-matrix library (the most popular WebGL math library) provides mat4.create(), mat4.translate(), mat4.rotate(), mat4.scale(), mat4.multiply(), mat4.perspective(), and mat4.lookAt() functions that handle all the matrix operations efficiently using Float32Arrays.
Three.js manages matrices through its scene graph. Each Object3D has a matrix property (local transform) and a matrixWorld property (combined with parent transforms). You set position, rotation, and scale through properties, and the engine computes matrices automatically when matrixAutoUpdate is true (the default). For performance-critical code, set matrixAutoUpdate = false and call updateMatrix() manually only when the object has actually changed.
Babylon.js follows a similar pattern with its TransformNode class. Each node exposes position, rotation, and scaling properties, and the engine computes world matrices through the scene graph hierarchy. Babylon.js uses a left-handed coordinate system by default (z-axis points into the screen), while Three.js uses right-handed (z-axis points toward the viewer). This distinction affects rotation direction and cross product orientation but does not change the fundamental matrix math.
Common Matrix Operations
Inverse matrix: The inverse of a transformation matrix undoes the transformation. If matrix M moves an object to position (10, 5), the inverse of M moves it back to the origin. Inverses are used to compute the view matrix from the camera's world matrix, to transform screen coordinates back to world coordinates (mouse picking), and to transform between coordinate spaces of different objects. Computing a 4x4 inverse is expensive (many multiplications), so cache it when possible.
Transpose matrix: The transpose of a matrix flips rows and columns. For orthogonal matrices (rotation matrices where all axes are unit length and perpendicular), the transpose equals the inverse, which is much cheaper to compute. This optimization is available whenever you know a matrix represents pure rotation with no scaling.
Matrix decomposition: Extracting the individual position, rotation, and scale components from a combined matrix. Three.js provides matrix.decompose(position, quaternion, scale) for this. Decomposition is useful when you receive a world matrix from a physics engine or animation system and need to read back the individual transform components for game logic.
Matrices let you combine scale, rotation, and translation into a single operation applied once per vertex. The model-view-projection pipeline transforms objects from local space to screen pixels through three matrix multiplications. Game engines manage this internally, but understanding the pipeline helps you debug visual issues and implement custom effects.