Math.acos()

Returns the arccosine of a number in radians, the inverse of Math.cos().

Signature
Math.acos(x)
  • Added in JavaScript ES1

Parameters

ParameterTypeDescription
$xnumberA number between -1 and 1, inclusive, representing the angle's cosine value.

Return value

numberThe inverse cosine — an angle in radians between 0 and π, inclusive — of x. If x is less than -1 or greater than 1, returns NaN.

Math.acos(x) undoes Math.cos(): given a cosine value it returns the angle, in radians, between 0 and π.

When to reach for it

Its single most common job is turning a normalized dot product into an angle. Because a · b = |a| |b| cos θ, the angle between two vectors is Math.acos(dot / (magA * magB)). That formula shows up in lighting calculations, in measuring how far apart two embeddings point, in constraining a joint's rotation, and in the spherical law of cosines for distance on a globe.

In plain trigonometry it recovers an angle from the adjacent side over the hypotenuse.

What trips people up

The domain is [-1, 1] and violations return NaN silently. This bites hardest in the dot-product formula: for two vectors pointing the same way the ratio is mathematically exactly 1, but floating-point division routinely lands on 1.0000000000000002, and the angle between a vector and itself becomes NaN. Always clamp.

The output range never goes negative, so Math.acos() reports the unsigned separation between directions. It cannot say whether the second vector is clockwise or counter-clockwise from the first — for that you need Math.atan2().

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Measure the angle between two vectors from their dot product
  • Recover an angle from the adjacent side and hypotenuse
  • Compute the diffuse lighting term for a surface normal
  • Find great-circle angular separation with the spherical law of cosines

Frequently asked questions

Why does Math.acos() return NaN for identical vectors?

The normalized dot product should be exactly 1 but floating-point division often yields 1.0000000000000002, which is outside the [-1, 1] domain. Clamp with Math.min(1, Math.max(-1, ratio)) before calling Math.acos().

What is the range of Math.acos()?

Math.acos() returns a value from 0 to π radians, or 0 to 180 degrees. It never returns a negative angle, so it gives the unsigned separation between two directions and cannot tell you which way one is rotated from the other.

How do I get the angle between two vectors in JavaScript?

Divide their dot product by the product of their magnitudes, clamp the result to [-1, 1], and pass it to Math.acos(). Multiply by 180 / Math.PI if you need degrees.

More examples

Angle between two vectors, safely

For [3, 4, 5] against itself the ratio rounds to 0.9999999999999999 — just *below* 1 — so the clamp is not what saves this particular line; acos returns 1.49e-8 radians, which still prints as 0.00. The clamp is there for the cases where the same division lands on 1.0000000000000002 instead, which would give NaN. The other results are 45, 90 and 180 degrees as expected.

Example · javascript
function angleBetween(a, b) {
  const dot = a.reduce((s, v, i) => s + v * b[i], 0);
  const ratio = dot / (Math.hypot(...a) * Math.hypot(...b));
  return Math.acos(Math.min(1, Math.max(-1, ratio)));
}

const deg = (r) => ((r * 180) / Math.PI).toFixed(2);
console.log(deg(angleBetween([1, 0], [1, 1])));
console.log(deg(angleBetween([1, 0], [0, 1])));
console.log(deg(angleBetween([1, 0], [-1, 0])));
console.log(deg(angleBetween([3, 4, 5], [3, 4, 5]))); // identical

Domain edges

Both out-of-domain calls give NaN rather than throwing. The last two lines show the identity acos(x) + asin(x) = π/2 holding only approximately: the sum prints 1.5707963267948968 against Math.PI / 2's 1.5707963267948966, one unit in the last place apart, so compare it with an epsilon rather than ===.

Example · javascript
console.log(Math.acos(-2), Math.acos(2));
console.log(Math.acos(0.5));   // pi/3
console.log(Math.acos(-0.5));  // 2pi/3

// acos and asin are complements
const x = 0.3;
console.log(Math.acos(x) + Math.asin(x));
console.log(Math.PI / 2);

Diffuse lighting term

With unit vectors the dot product is already the cosine, so the intensity comes straight from it and Math.acos() is only needed for the human-readable angle: 0, 45 and 180 degrees. Light from behind gives a negative cosine, clamped to zero intensity.

Example · javascript
function lambert(normal, light) {
  const dot = normal.reduce((s, v, i) => s + v * light[i], 0);
  const cosTheta = Math.min(1, Math.max(-1, dot));
  const angleDeg = (Math.acos(cosTheta) * 180) / Math.PI;
  return { angle: +angleDeg.toFixed(1), intensity: Math.max(0, cosTheta) };
}

console.log(lambert([0, 1, 0], [0, 1, 0]));
console.log(lambert([0, 1, 0], [0.7071, 0.7071, 0]));
console.log(lambert([0, 1, 0], [0, -1, 0]));

Discussion

  • Be the first to comment on this lesson.