The Cross Product & Working in 3D

Linear Algebra Fundamentals

Chapter 3 · The Cross Product & Working in 3D

Chapter 2's dot product takes two vectors and returns a single number. The cross product is a different kind of operation entirely: it takes two 3D vectors and returns a third vector — one that's perpendicular to both of the originals. It's specifically a 3D operation, which is itself worth understanding, not just the formula.

The Formula

For a = [a₁, a₂, a₃] and b = [b₁, b₂, b₃], the cross product is:

Cross product formula
a × b = [a₂b₃ − a₃b₂, a₃b₁ − a₁b₃, a₁b₂ − a₂b₁]

It looks arbitrary at first, but there's a pattern: each output component skips the matching input index (the first output component has no a₁/b₁ in it, and so on), and the two products in each pair are subtracted in a "cross" pattern. Chapter 7's own determinant will give this same formula a cleaner, more memorable form once determinants are available.

def cross(a, b): return [ a[1]*b[2] - a[2]*b[1], a[2]*b[0] - a[0]*b[2], a[0]*b[1] - a[1]*b[0], ] i = [1, 0, 0] j = [0, 1, 0] print(cross(i, j)) # [0, 0, 1] — this is exactly the identity i x j = k

The Right-Hand Rule

The direction of a × b is found with the right-hand rule: point your right hand's fingers along a, curl them toward b, and your thumb points in the direction of the result. This is also why order matters — the cross product is anti-commutative:

a × b = −(b × a)
Swapping the order flips the sign of every term in the formula, which reverses the resulting vector's direction while leaving its length unchanged. i × j = [0, 0, 1], but j × i = [0, 0, −1] — same axis, opposite direction.

Geometric Meaning: Perpendicular, With a Meaningful Length

Two things are true about a × b at once, and both matter in practice:

  1. Direction: it's perpendicular to both a and b — confirmed by the fact that (a × b) · a = 0 and (a × b) · b = 0 always hold, using Chapter 2's own dot product as the perpendicularity test.
  2. Magnitude: |a × b| = |a| |b| sin(θ) — which is exactly the area of the parallelogram that a and b span.

Worked example: a = [2, 1, 0], b = [1, 0, 2].

QuantityValue
a × b[2, −4, −1]
(a × b) · a0 ✓ perpendicular to a
(a × b) · b0 ✓ perpendicular to b
|a × b|√(2² + 4² + 1²) = √21 ≈ 4.583
|a| |b| sin(θ)√5 × √5 × 0.9165 ≈ 4.583 — an exact match

Testing for Parallel Vectors

Since sin(θ) = 0 exactly when θ is 0° or 180°, the cross product of two parallel (or anti-parallel) vectors is always the zero vector, regardless of their lengths. This gives a direct, practical test: if a × b = [0, 0, 0], the two vectors point along the same line.

Floating-point reality check
In real code, comparing a computed cross product to exactly [0, 0, 0] is fragile — rounding error means "should be zero" often comes out as something like [1e-16, -3e-17, 0] instead. The practical version of this test checks whether the magnitude of the cross product is below a small tolerance, not whether it's exactly zero.

2D vs. 3D: There's No True 2D Cross Product

The cross product as defined above genuinely requires three dimensions — "perpendicular to both a and b" only pins down a unique direction (up to sign) when there's a third axis to be perpendicular into. In 2D, a lot of code still uses a "cross product," but it's really a shortcut scalar, not a true cross product:

The 2D pseudo-cross-product
For 2D vectors a = [a₁, a₂] and b = [b₁, b₂], extending both into 3D with a zero third component ([a₁, a₂, 0]) and taking the real cross product leaves only a z-component: a₁b₂ − a₂b₁. That single number is what 2D code usually means by "the cross product" — it's really the z-component of the true 3D cross product, and its sign tells you whether b is a clockwise or counter-clockwise turn from a. Reusing Chapter 2's own worked vectors a = [3, 4], b = [1, 2]: a₁b₂ − a₂b₁ = 3×2 − 4×1 = 2, which matches extending both to [3, 4, 0] and [1, 2, 0] and computing the real cross product — [0, 0, 2].

Where This Shows Up: Surface Normals

In 3D graphics, a flat triangular face is usually stored as three corner points. To light that face correctly, the renderer needs its normal — a vector pointing straight out from the surface. That's computed by taking two of the triangle's edges as vectors and crossing them:

Why the order of the two edge vectors matters
Given edges u = [2, 0, 0] and v = [0, 3, 0], u × v = [0, 0, 6] — pointing out of the page. But v × u = [0, 0, −6] — pointing straight into it. This is exactly why 3D modelling tools like Blender care about a face's winding order (the order its corner points are listed in): it determines which way u × v points, which determines which side of the surface is treated as the "front" for lighting and backface culling.

Cross Products in Code — NumPy

import numpy as np a = np.array([2, 1, 0]) b = np.array([1, 0, 2]) print(np.cross(a, b)) # [ 2 -4 -1] print(np.linalg.norm(np.cross(a, b))) # 4.58257569... (area of the parallelogram)

Hands-On Exercises

Exercise 1

Compute e × f by hand for e = [1, -2, 3] and f = [2, 0, -1], showing each component's calculation. Then verify your result is genuinely perpendicular to both e and f by computing the two dot products.

📄 View solution
Exercise 2

Given g = [2, 4, -2] and h = [-1, -2, 1], use the cross product to determine whether they're parallel. Show the calculation, and separately confirm your conclusion by checking whether one vector is a scalar multiple of the other.

📄 View solution
Exercise 3

A triangular face has two edge vectors u = [2, 0, 0] and v = [0, 3, 0]. Compute the surface normal as u × v, then compute it the other order as v × u. Explain, in terms of the right-hand rule, why a 3D modelling tool listing this triangle's corner points in the opposite order would flip which side of the surface is treated as the front.

📄 View solution

Chapter 3 Quick Reference

  • Cross product: a × b = [a₂b₃−a₃b₂, a₃b₁−a₁b₃, a₁b₂−a₂b₁] — a 3D-only operation returning a vector, not a scalar
  • Direction: perpendicular to both inputs, given by the right-hand rule; anti-commutativea × b = −(b × a)
  • Magnitude: |a × b| = |a||b|sin(θ) = the area of the parallelogram spanned by a and b
  • A zero cross product (within floating-point tolerance) means the two vectors are parallel or anti-parallel
  • There's no true 2D cross product — the "2D cross product" (a₁b₂ − a₂b₁) is a scalar shortcut equal to the z-component of the 3D version
  • Surface normals in graphics are computed by crossing two edge vectors of a face; the order of the edges (winding order) determines which way the normal points
  • Next chapter: Matrices — representation and basic operations