Math.sin()

ES1+

Returns the sine of an angle given in radians, a value oscillating between -1 and 1. As the fundamental periodic function, it converts steadily increasing input (time, angle, position) into smooth back-and-forth motion. Inputs in degrees must be converted first: radians = degrees * Math.PI / 180.

Syntax

Math.sin(x)

Parameters

x number

A number in radians

Return Value

number

The sine of the given number

Examples

JavaScript
console.log(Math.sin(0));
console.log(Math.sin(Math.PI / 2));
console.log(Math.sin(Math.PI));
Output:
// 0 1 1.2246467991473532e-16

📌 When to Use

Use Math.sin() whenever you need smooth oscillation or the vertical component of circular motion: floating and bobbing UI animations, pulsing highlights, pendulums and springs in game physics, wave and water effects, audio synthesis (a sine at frequency f is Math.sin(2 * Math.PI * f * t)), plotting periodic data, and, with Math.cos(), placing points on circles and arcs (y = cy + r * Math.sin(angle)). The recipe for animation is always the same shape: feed it time scaled to a frequency, then rescale the -1..1 output into your target range, offset + amplitude * Math.sin(t * speed + phase), where phase shifts let multiple elements oscillate out of step. In geometry it is the y-side of the unit-circle pair; in signal work it is the basis function everything else decomposes into. Always be explicit about units: JavaScript trig is radians-only, and the degree-to-radian conversion belongs in a named helper, not sprinkled inline. For the related but distinct jobs, easing a one-shot transition (use an easing function or CSS), rotating vectors (use both sin and cos), or recovering angles (use Math.asin or, almost always better, Math.atan2), reach for the right neighbor instead of bending sine to fit.

⚠️ Common Mistakes

Passing degrees: Math.sin(90) is 0.8939... (the sine of 90 radians), not 1. Nothing errors, animations just look wrong, so unit bugs survive embarrassingly long. Convert with deg * Math.PI / 180, or keep everything in radians end to end.

Expecting exact zeros and ones: Math.sin(Math.PI) is 1.2246467991473532e-16, not 0, because Math.PI itself is the nearest double to π, and sine faithfully evaluates that slightly-off input. Comparisons need tolerances, and code branching on sin(x) === 0 never triggers.

Feeding enormous arguments: Math.sin(1e15) requires reducing 1e15 modulo 2π, and at that magnitude adjacent doubles are further apart than the function's period, so results are effectively meaningless noise; results may even differ across engines since correctly rounded trig is not required by the spec. Wrap accumulating angles: angle %= 2 * Math.PI.

Accumulating phase by repeated addition: angle += 0.016 * speed each frame drifts with floating error and frame jitter; deriving phase from absolute time (Math.sin(now / 1000 * speed)) keeps oscillation frequency exact and animation frame-rate independent.

Using sine where an easing curve belongs: sine oscillates forever, while a hover transition should run once and settle; conversely, chaining easing functions to fake oscillation is more code than one sin call. Match the tool to one-shot versus periodic motion.

Recovering angles with Math.asin alone: it returns only -π/2..π/2, collapsing quadrants; reconstructing direction from components needs Math.atan2(y, x), the function designed to preserve quadrant information.

✅ Best Practices

Centralize unit conversion: const degToRad = (d) => d * Math.PI / 180 (and its inverse), used at every boundary where designers or APIs speak degrees; inside the codebase, standardize on radians.

Parameterize oscillations explicitly: offset + amplitude * Math.sin(2 * Math.PI * frequencyHz * timeSeconds + phase) makes speed, range, and alignment independently tunable, and encodes the frame-rate-independent time-based form.

Keep long-lived angles bounded: reduce with angle = angle % (2 * Math.PI) (or track time and derive) so precision does not degrade over hours of runtime, particularly in kiosk-style always-on animations.

Compare trig results with tolerance helpers (Math.abs(a - b) < 1e-9 scaled to context), and prefer formulations that avoid catastrophic cancellation, for tiny angles, sin(x) ≈ x is often the numerically honest simplification.

Prefer platform animation where it fits: CSS keyframes or the Web Animations API run compositor-side without JavaScript per frame; reserve requestAnimationFrame sine loops for motion that genuinely depends on runtime state, and drive them from the rAF timestamp argument.

⚡ Performance Notes

Math.sin() is a transcendental function costing tens of nanoseconds, several times a multiplication, but still cheap enough that a few hundred calls per frame is nothing: the old advice to precompute lookup tables is obsolete for typical web workloads, where table memory traffic and interpolation code often cost more than the native call, and modern engines evaluate sin very efficiently. Reserve tables or polynomial approximations for extreme cases, per-sample audio synthesis in tight ScriptProcessor-style loops or hundreds of thousands of calls per frame, and prefer moving such kernels to WebAssembly or AudioWorklets with typed arrays anyway. Argument magnitude affects speed and accuracy: keeping angles reduced near zero avoids the slow huge-argument reduction path. And per the spec, implementations may differ in the last bits, so never build logic on exact trig equality across engines.

🌍 Real World Example

Smooth Hover Animation

The gentle float of a hero image, badge, or onboarding mascot is one sine wave applied to translateY: this example derives phase from the requestAnimationFrame timestamp, so motion speed is identical on 60 Hz and 144 Hz displays, and maps the -1..1 output to a pixel amplitude around the element's resting position. A per-element phase offset makes groups drift pleasantly out of sync instead of marching in lockstep. The same construction pulses notification dots, sways background shapes, and animates breathing exercises in wellness apps.

function createFloatingAnimation(element, options = {}) {
  const { amplitude = 10, frequency = 1, startTime = Date.now() } = options;

  function animate() {
    const elapsed = (Date.now() - startTime) / 1000;
    const offset = Math.sin(elapsed * frequency * Math.PI * 2) * amplitude;

    element.style.transform = `translateY(${offset}px)`;
    requestAnimationFrame(animate);
  }

  animate();
}

// Usage: createFloatingAnimation(document.querySelector('.floating-icon'));
// Creates a smooth up-and-down floating effect

Related Methods