Vectors and Dot Product for Game Developers

Updated July 2026
Vectors are the most fundamental math concept in game development, used for every position, direction, velocity, and force in your game world. A 2D vector stores an x and y component, a 3D vector adds z, and the operations you perform on them (addition, subtraction, normalization, dot product, cross product) solve the majority of spatial problems you encounter when building games. Understanding vectors deeply is the single highest-leverage math investment a game developer can make.

What a Vector Represents

A vector is an ordered set of numbers that represents two things simultaneously: a direction and a magnitude (length). In 2D games, a vector has two components: (x, y). In 3D games, three components: (x, y, z). The vector (3, 4) points to the right and upward from the origin, and its magnitude is 5 (by the Pythagorean theorem: the square root of 3 squared plus 4 squared).

Vectors serve two distinct purposes in games, and keeping them straight prevents confusion. A position vector represents a point in space: the character is at (200, 350) in screen coordinates. A direction vector represents a displacement or velocity: the character is moving at (5, -2) pixels per frame, meaning 5 pixels right and 2 pixels up each frame. The same data structure holds both types, but the operations you perform differ. You add a velocity vector to a position vector to get a new position. You do not add two position vectors together (that would not be meaningful).

Every game framework represents vectors internally. Phaser has Phaser.Math.Vector2. Three.js has THREE.Vector3. Babylon.js has BABYLON.Vector3. PixiJS stores position as a point with x and y properties. Even if you never create a vector object explicitly, you are working with vectors every time you set a position, define a velocity, or calculate a direction.

Essential Vector Operations

Addition combines two vectors component by component. If a character is at position (100, 200) and their velocity is (5, -3), the new position after one frame is (100 + 5, 200 + (-3)) = (105, 197). Vector addition is how movement works in every game. Position plus velocity times delta time equals new position. This single operation drives character movement, projectile flight, camera panning, and particle motion.

Subtraction finds the vector from one point to another. If the player is at (300, 400) and an enemy is at (100, 200), the vector from the enemy to the player is (300 - 100, 400 - 200) = (200, 200). This difference vector tells you both the direction (up and to the right at 45 degrees) and the distance (magnitude of about 283 pixels). You use subtraction whenever you need to know which direction to aim, how far away a target is, or the displacement between any two points.

Scaling multiplies every component by a scalar value. The vector (4, 3) scaled by 2 becomes (8, 6). Scaling by 0.5 gives (2, 1.5). Scaling by -1 reverses the direction: (-4, -3). Scaling controls speed: if your velocity is (3, 4) and you want to move twice as fast, scale it by 2. If you want to move in the opposite direction, scale by -1. If you want to slow down by 10% each frame (friction), scale by 0.9.

Magnitude (length) measures how long a vector is. For a 2D vector (x, y), the magnitude is Math.sqrt(x*x + y*y). For 3D, add z*z inside the square root. Magnitude tells you the distance between two points (after subtracting their position vectors) and the speed of a moving object (the magnitude of its velocity vector). Because Math.sqrt is relatively expensive, games often compare squared magnitudes when they only need to know which of two distances is shorter, avoiding the square root entirely.

Normalization converts any vector into a unit vector: a vector pointing in the same direction but with a magnitude of exactly 1. You normalize by dividing each component by the magnitude: (x / mag, y / mag). A normalized vector represents a pure direction with no magnitude attached. This is essential when you want to move at a specific speed in a specific direction: normalize the direction vector, then scale it by the desired speed. Without normalization, diagonal movement (where both x and y components are nonzero) would be faster than horizontal or vertical movement.

The Dot Product

The dot product is the most useful single operation in game math. Given two vectors A and B, the dot product is A.x * B.x + A.y * B.y (plus A.z * B.z in 3D). The result is a single number, not a vector. This number equals the product of the two magnitudes times the cosine of the angle between the vectors: dot(A, B) = |A| * |B| * cos(angle).

When both vectors are normalized (unit vectors), the dot product simplifies to just the cosine of the angle between them. This gives you a fast angle test without computing any actual angles. A dot product of 1 means the vectors point in the same direction. A dot product of 0 means they are perpendicular (90 degrees apart). A dot product of -1 means they point in opposite directions. Values between these extremes indicate intermediate angles.

Facing checks: To determine if an enemy is in front of the player, normalize both the player's forward direction vector and the vector from the player to the enemy, then take the dot product. If the result is positive, the enemy is in the forward hemisphere. If greater than 0.7 (roughly 45 degrees), the enemy is within a 90-degree forward cone. This is faster and simpler than computing actual angles with arctangent.

Lighting: In 3D rendering, the brightness of a surface under directional light equals the dot product of the surface normal and the light direction (both normalized). A surface facing directly toward the light gets full brightness (dot product = 1). A surface perpendicular to the light gets zero brightness (dot product = 0). A surface facing away is in shadow (dot product is negative, clamped to 0). This is the foundation of diffuse lighting in every 3D renderer, including WebGL shaders.

Projection: The scalar projection of vector A onto vector B tells you how far A extends in the direction of B. The formula is dot(A, B) / magnitude(B), or simply dot(A, B) when B is normalized. This operation appears in collision response (projecting velocity onto a collision normal to separate overlapping objects), camera systems (projecting distance onto the camera's forward axis), and shadow calculations.

Reflection: When a ball bounces off a wall, the reflected velocity uses the formula R = V - 2 * dot(V, N) * N, where V is the incoming velocity, N is the normalized wall normal, and R is the reflected velocity. This single formula handles bouncing off walls at any angle and works identically in 2D and 3D.

The Cross Product

The cross product is a 3D operation that takes two vectors and produces a third vector perpendicular to both. The result's magnitude equals the area of the parallelogram formed by the two input vectors. Games use the cross product for calculating surface normals (which direction a polygon faces), determining winding order (is this triangle facing the camera or away from it?), and computing torque (rotational force around an axis).

In 2D, the cross product concept reduces to a single scalar value: A.x * B.y - A.y * B.x. This value tells you the signed area of the parallelogram, which indicates whether B is clockwise or counterclockwise relative to A. A positive result means B is counterclockwise (to the left). A negative result means clockwise (to the right). Zero means the vectors are parallel or antiparallel. This 2D cross product is useful for determining which side of a line a point falls on, which direction to steer an AI agent, and computing signed areas of polygons.

Common Game Development Patterns

Moving toward a target: Subtract the current position from the target position to get the direction vector. Normalize it. Scale it by the desired speed. Add it to the current position each frame. This pattern drives enemy AI seeking behavior, homing missiles, and any object that moves toward a destination.

Distance checking: Subtract two position vectors, then compute the magnitude of the result. Compare this distance to a threshold. This checks if two objects are within interaction range, attack range, or pickup range. For performance, compare squared distances to a squared threshold to avoid the square root.

Diagonal speed correction: When the player presses right and up simultaneously, the raw input vector is (1, 1), which has a magnitude of about 1.414. If you apply this directly, diagonal movement is 41% faster than cardinal movement. The fix is to normalize the input vector before scaling it by the movement speed. The normalized vector (0.707, 0.707) has magnitude 1, so the player moves at the same speed in every direction.

Separating overlapping objects: When collision detection finds two circles overlapping, subtract their positions to get the direction between centers. Normalize this direction to get the separation axis. Calculate the overlap distance (sum of radii minus distance between centers). Move each object half the overlap distance along the separation axis in opposite directions. This is the simplest collision resolution and it works cleanly for circles and spheres.

Field of view: To check if a target is within a character's field of view (say, a 120-degree cone), normalize the character's forward direction and the direction to the target. Take the dot product. If the result is greater than the cosine of half the FOV angle (cos(60 degrees) = 0.5 for a 120-degree cone), the target is within the field of view. This is a single dot product comparison instead of computing and comparing actual angles.

Key Takeaway

Vectors handle position, movement, direction, and distance. The dot product handles facing checks, lighting, projection, and reflection. Normalization ensures consistent speed regardless of direction. These three concepts solve 80% of all spatial math problems in game development.