Math.cos()
ES1+Returns the cosine of an angle given in radians, oscillating between -1 and 1 and starting at 1 when the angle is 0. It is the horizontal partner of Math.sin(): together they convert an angle into the x and y coordinates of a point on the unit circle.
Syntax
Math.cos(x)Parameters
x number A number in radians
Return Value
The cosine of the given number
Examples
console.log(Math.cos(0));
console.log(Math.cos(Math.PI));
console.log(Math.cos(Math.PI * 2)); 📌 When to Use
Use Math.cos() for the x-component of anything circular and for oscillations that must start at their peak: positioning items around a radial menu or clock face (x = cx + r * Math.cos(angle), with sin supplying y), orbiting particles and satellites in games, drawing arcs and pie-chart segment boundaries, rotating vectors (the 2D rotation formula uses both cos and sin), computing horizontal projections of angled motion (velocityX = speed * Math.cos(heading)), and dot-product-based angle work in graphics, where the cosine of the angle between unit vectors is their dot product, the basis of lighting calculations and "is this facing the camera" tests. Cosine is sine shifted a quarter turn, cos(x) equals sin(x + π/2), so choosing between them is really choosing the starting phase: cos starts at maximum (useful when an animation should begin fully extended), sin starts at zero (begin at rest). In screen coordinates remember the y-axis points down, so angles increase clockwise visually; the math is unchanged, but labels like "counterclockwise" flip, a perennial source of confusion in canvas code. As with all JavaScript trig, inputs are radians, and recovering angles from coordinates belongs to Math.atan2(y, x), not to acos except when working from dot products.
⚠️ Common Mistakes
Swapping the sin/cos roles in coordinate math: x pairs with cosine and y with sine (for angles measured from the positive x-axis); interchanging them rotates everything by 90° and mirrors motion, which can look almost right, radial layouts come out subtly rotated, and the bug persists.
Degrees again: Math.cos(60) is not 0.5, Math.cos(Math.PI / 3) is (0.5000000000000001, see the next item). Every trig call site in a codebase should make its unit obvious, ideally by only ever handling radians internally.
Expecting clean values at textbook angles: Math.cos(Math.PI / 2) is 6.123233995736766e-17, not 0, because π/2 is not exactly representable. Code that tests cos(x) === 0 to detect verticals never fires; use thresholds, or restructure to avoid the comparison.
Ignoring screen-coordinate orientation: with y increasing downward, a positive angle sweep renders clockwise; copying math-convention formulas without flipping (or without accepting the flipped interpretation) produces arcs that sweep the wrong way and pie charts that fill backward.
Using Math.acos on dot products without clamping: accumulated float error pushes normalized dot products to 1.0000000000000002, and acos of that is NaN, the classic lighting/rotation NaN source. Clamp to [-1, 1] before acos.
Deriving direction from acos of an x-component: acos returns 0..π only, losing the sign of y; full direction recovery is Math.atan2(y, x). Reserve acos for genuine "angle between" magnitude questions.
✅ Best Practices
Encapsulate polar-to-Cartesian conversion once: pointOnCircle(cx, cy, r, angle) returning { x: cx + r * Math.cos(angle), y: cy + r * Math.sin(angle) }, so the sin/cos pairing and any y-axis flip live in one audited function.
When placing n items on a circle, distribute angles as i * (2 * Math.PI / n) with an explicit starting offset (subtract π/2 to start at 12 o'clock), the offset requirement is otherwise discovered by visual debugging.
Rotate vectors with the full formula, x' = x·cosθ - y·sinθ, y' = x·sinθ + y·cosθ, computed from one shared cosθ and sinθ pair; recomputing trig per component wastes work and risks inconsistent values.
For SVG arcs and conic gradients, prefer declarative primitives (path arc commands, conic-gradient) where possible; hand-computed cos/sin point lists are for cases needing runtime data, like the progress ring in this example.
Cache cosθ/sinθ when the same angle applies to many points (rotating a polygon, a particle system's emitter direction): two trig calls plus n multiplies beats 2n trig calls, an easy and legitimate micro-optimization.
⚡ Performance Notes
Math.cos() costs the same as Math.sin(), tens of nanoseconds per call, inlined-native and irrelevant below thousands of calls per frame. The realistic optimizations are structural: hoist the cos/sin of a shared angle out of per-point loops (rotation matrices exist precisely to reuse them), derive per-frame phase from the rAF timestamp rather than accumulating, and keep angles reduced to modest magnitudes to stay off the slow argument-reduction path and away from precision loss. When both sine and cosine of the same angle are needed, compute both once and pass them together; JavaScript lacks a combined sincos API, but the two calls still beat recomputation scattered across functions. For genuinely trig-bound workloads (spectrograms, large particle fields), typed arrays plus WebAssembly, or precomputed geometry reused across frames, deliver order-of-magnitude wins that no sin/cos micro-tuning can match.
🌍 Real World Example
Circular Progress Indicator
Circular progress rings, the timers, fitness dials, and loading indicators of modern UIs, are drawn by converting a progress fraction into an angle and then into a point on the circle with cos and sin. This example computes the arc endpoint for an SVG path, applying the start-at-top offset and demonstrating the polar-to-Cartesian helper every radial component ends up needing. The same conversion lays out radial menus, positions hour markers on clock faces, animates orbiting decorations, and generates the coordinates behind pie and donut charts.
function calculateCircularProgress(percentage, radius = 50, centerX = 50, centerY = 50) {
// Convert percentage to angle (starting from top, clockwise)
const startAngle = -Math.PI / 2;
const endAngle = startAngle + (percentage / 100) * Math.PI * 2;
const startX = centerX + radius * Math.cos(startAngle);
const startY = centerY + radius * Math.sin(startAngle);
const endX = centerX + radius * Math.cos(endAngle);
const endY = centerY + radius * Math.sin(endAngle);
const largeArcFlag = percentage > 50 ? 1 : 0;
return {
svgPath: `M ${startX} ${startY} A ${radius} ${radius} 0 ${largeArcFlag} 1 ${endX} ${endY}`,
endPoint: { x: endX, y: endY }
};
}
console.log(calculateCircularProgress(75));
// Generates SVG arc path for 75% progress