2. The dot product: angles and projections

📖 Reading · 10 min
💡 Every code box below is live — edit it and hit Run.

§9.0 built vectors and gave them a length, but no way to compare two vectors' directions against each other. The dot product fills that gap — a way to multiply two vectors together that produces a single number encoding exactly the angle between them, and from there, how much of one vector points along another.

Definition

\vec u\cdot\vec v=u_1v_1+u_2v_2+u_3v_3

— multiply matching components and add. The result is a scalar (an ordinary number), not a vector — this is what distinguishes it from the cross product in §9.2.

The geometric formula

The algebraic definition above is equivalent to a geometric one:

\vec u\cdot\vec v=|\vec u||\vec v|\cos\theta

where \theta is the angle between \vec u and \vec v. (This follows from the Law of Cosines applied to the triangle formed by \vec u, \vec v, and \vec u-\vec v — a fact from trigonometry, not new calculus.) Solving for \theta:

\theta=\arccos\left(\frac{\vec u\cdot\vec v}{|\vec u||\vec v|}\right)

This is the primary use of the dot product — it converts a purely algebraic computation (multiply and add components) into a geometric fact (the angle between two directions), with no trigonometry needed on the vectors' own coordinates.

Orthogonality

Since \cos\theta=0 exactly when \theta=90°:

\vec u\cdot\vec v=0\iff\vec u\text{ and }\vec v\text{ are orthogonal (perpendicular)}

— for nonzero vectors. This single equation is the fastest perpendicularity test available, replacing any need to compute an actual angle when all that's needed is a yes/no answer. It's also exactly the algebraic condition behind orthogonal functions in §4.7 and §8.5's Fourier series — "dot product zero" is the same idea as "integral of the product is zero," just for vectors instead of functions, and both go by the same name for precisely that reason.

Sign carries meaning too: \vec u\cdot\vec v>0 means the angle is acute (the vectors broadly point the same way), \vec u\cdot\vec v<0 means obtuse (broadly opposite), =0 means exactly perpendicular.

Projections

The scalar projection of \vec u onto \vec v measures how far \vec u reaches in the direction of \vec v — the length of \vec u's "shadow" cast along \vec v:

\text{comp}_{\vec v}\vec u=\frac{\vec u\cdot\vec v}{|\vec v|}

(this follows directly from the geometric formula: $\vec u\cdot\vec v=|\vec u|\cos\theta\cdot|\vec v|$, and |\vec u|\cos\theta is exactly the adjacent-side length in a right triangle with hypotenuse |\vec u|).

The vector projection turns that shadow length into an actual vector, by scaling the unit vector \hat v:

\text{proj}_{\vec v}\vec u=\text{comp}_{\vec v}\vec u\cdot\hat v=\frac{\vec u\cdot\vec v}{|\vec v|^2}\vec v

Projection is a genuinely practical operation, not just a geometric curiosity: it's how work in physics gets computed when force and displacement point in different directions (only the component of force along the direction of motion does any work — §5.4's constant-direction case is the special case where force and displacement are already parallel), and it reappears directly in §9.6 as the mechanism for decomposing acceleration into components.

Doing it in Python

The angle between two vectors, computed from the geometric formula:

import sympy as sp

u = sp.Matrix([1, 1, 0])
v = sp.Matrix([1, 0, 1])

dot = u.dot(v)
cos_theta = dot / (sp.sqrt(u.dot(u)) * sp.sqrt(v.dot(v)))
theta = sp.acos(cos_theta)

print(f"u . v = {dot}")
print(f"cos(theta) = {cos_theta}")
print(f"theta = {theta} = {float(theta):.6f} rad = {float(sp.deg(theta)):.1f} degrees")

Checking orthogonality directly, and confirming the sign of the dot product tracks acute vs. obtuse:

import sympy as sp

pairs = [
    (sp.Matrix([1, 2, 2]), sp.Matrix([2, -2, 1])),   # orthogonal
    (sp.Matrix([1, 1, 0]), sp.Matrix([1, 1, 1])),     # acute
    (sp.Matrix([1, 0, 0]), sp.Matrix([-1, 1, 0])),    # obtuse
]

for u, v in pairs:
    d = u.dot(v)
    kind = "orthogonal" if d == 0 else ("acute" if d > 0 else "obtuse")
    print(f"u={u.T}, v={v.T}: u.v = {d}  ({kind})")

Scalar and vector projection of \vec u=\langle4,3\rangle onto \vec v=\langle1,1\rangle:

import sympy as sp

u = sp.Matrix([4, 3])
v = sp.Matrix([1, 1])

scalar_proj = u.dot(v) / sp.sqrt(v.dot(v))
vector_proj = (u.dot(v) / v.dot(v)) * v

print(f"scalar projection comp_v(u) = {scalar_proj} = {float(scalar_proj):.4f}")
print(f"vector projection proj_v(u) = {vector_proj.T}")

Worked example

Find the angle between \vec u=\langle1,1,0\rangle and \vec v=\langle1,0,1\rangle.

\vec u\cdot\vec v=(1)(1)+(1)(0)+(0)(1)=1

|\vec u|=\sqrt{1^2+1^2+0^2}=\sqrt2,\qquad|\vec v|=\sqrt{1^2+0^2+1^2}=\sqrt2

\cos\theta=\frac1{\sqrt2\cdot\sqrt2}=\frac12

\theta=\arccos\frac12=\boxed{\frac\pi3}=60°

Sanity check. Both vectors have the same length (\sqrt2) and share one component pattern (one coordinate is 1, the other is 0, in different slots) — a symmetric-looking pair like this landing on a "nice" angle like 60° rather than some ugly irrational-looking angle is a reasonable outcome for such a clean, symmetric setup. ✓ And 60° is comfortably acute, consistent with the dot product coming out positive (1>0).

Your turn

1. Determine whether \vec u=\langle3,-2,1\rangle and \vec v=\langle1,2,1\rangle are orthogonal.

2. Find the scalar and vector projection of \vec u=\langle2,4\rangle onto \vec v=\langle3,0\rangle (a projection onto a purely horizontal vector — check that your answer makes intuitive sense).

3. True or false: \vec u\cdot\vec v=\vec v\cdot\vec u for all vectors \vec u,\vec v.

Solutions

1. \vec u\cdot\vec v=(3)(1)+(-2)(2)+(1)(1)=3-4+1=0.

\boxed{\text{orthogonal}}

2. \vec u\cdot\vec v=(2)(3)+(4)(0)=6, |\vec v|=3.

\text{comp}_{\vec v}\vec u=\frac63=\boxed2

\text{proj}_{\vec v}\vec u=\frac6{9}\langle3,0\rangle=\boxed{\langle2,0\rangle}

Sanity check on the intuition: projecting onto a purely horizontal vector should just extract \vec u's x-component and zero out the y — and indeed \langle2,0\rangle is exactly \vec u=\langle2,4\rangle's horizontal part, with the 4 discarded entirely. ✓

3. True. $\vec u\cdot\vec v=u_1v_1+u_2v_2+u_3v_3=v_1u_1+v_2u_2+v_3u_3=\vec v\cdot\vec u$ — ordinary multiplication of real numbers commutes, and the dot product is built entirely from componentwise real-number multiplication, so the commutativity transfers directly. (This is not true of the cross product, coming next in §9.2 — a fact worth flagging now, before that lesson's very different answer arrives.)

Check yourself in code

Compute the dot product, magnitudes, and angle between \vec u=\langle1,1,0\rangle and \vec v=\langle1,0,1\rangle.

Print exactly this:

u . v = 1
cos(theta) = 1/2
theta = pi/3
import sympy as sp

u = sp.Matrix([1, 1, 0])
v = sp.Matrix([1, 0, 1])

dot = u.dot(v)
cos_theta = dot / (sp.sqrt(u.dot(u)) * sp.sqrt(v.dot(v)))
theta = sp.acos(cos_theta)
print("u . v = ...")
print("cos(theta) = ...")
print("theta = ...")
import sympy as sp

u = sp.Matrix([1, 1, 0])
v = sp.Matrix([1, 0, 1])

dot = u.dot(v)
cos_theta = dot / (sp.sqrt(u.dot(u)) * sp.sqrt(v.dot(v)))
theta = sp.acos(cos_theta)
print(f"u . v = {dot}")
print(f"cos(theta) = {cos_theta}")
print(f"theta = {theta}")

The dot product, \vec u\cdot\vec v=u_1v_1+u_2v_2+u_3v_3, equals |\vec u||\vec v|\cos\theta geometrically — turning a componentwise multiplication into the angle between two directions, with a zero result signaling perpendicularity in the same spirit as orthogonal functions from §4.7. Projection uses that same angle relationship to extract how much of one vector points along another, a genuinely physical operation that resurfaces when work is computed at an angle and again when acceleration gets decomposed in §9.6.

Next: a second way to multiply vectors — one that, unlike the dot product, produces another vector, perpendicular to both inputs, and measures area instead of angle.