Math.atan2()
Returns the angle in radians from the positive x-axis to the point (x, y), across all four quadrants.
Math.atan2(y, x)- Added in JavaScript ES1
Parameters
| Parameter | Type | Description |
|---|---|---|
$y | number | The y coordinate of the point. Note that this comes first. |
$x | number | The x coordinate of the point. |
Return value
numberThe angle in radians, between -π and π inclusive, between the positive x-axis and the ray from (0, 0) to the point (x, y).
Math.atan2(y, x) measures the counter-clockwise angle, in radians, from the positive x-axis to the point (x, y). The arguments are passed y first — that ordering matches the ratio y/x it generalizes, and reversing it silently mirrors every angle you compute.
When to reach for it
This is the function for turning a pair of coordinates into a direction: the heading from one entity toward another, the angle of a drag gesture, the rotation needed to aim a sprite, the bearing between two map points. Paired with Math.hypot() it converts Cartesian to polar in two calls.
Why not Math.atan(y / x)
Because the division throws away information. Math.atan() spans only (-π/2, π/2), so it cannot distinguish a point in quadrant one from its opposite in quadrant three. Math.atan2() sees both signs and returns the full [-π, π] range. It also survives the degenerate cases: Math.atan2(1, 0) is π/2 where 1/0 would need special handling, and Math.atan2(0, 0) is 0 where 0/0 is NaN. Signed zeros and infinities each produce a defined angle.
What trips people up
Results in quadrants three and four are negative. For a 0–360° compass bearing, normalize with ((deg % 360) + 360) % 360. And screen coordinates run y-downward, so an on-screen angle comes out mirrored unless you negate y.
Example
When to use it
- Compute the heading from one point toward another to aim or steer
- Convert Cartesian coordinates to polar alongside Math.hypot()
- Measure the rotation angle of a drag or swipe gesture
- Derive a compass bearing from an x/y offset
Frequently asked questions
Why is Math.atan2() called with y before x?
The argument order mirrors the ratio y/x that the function generalizes, and it is fixed by the specification. Math.atan2(y, x) returns the angle to the point (x, y), so passing coordinates in reading order silently reflects the result about the 45-degree line.
How do I get an angle between 0 and 360 degrees?
Convert the radians to degrees and normalize with ((deg % 360) + 360) % 360. Math.atan2() returns -π to π, so points below the x-axis produce negative angles that the double modulo folds into the 0–360 range.
When should I use Math.atan2() instead of Math.atan()?
Use Math.atan2() any time you have separate x and y components. It distinguishes all four quadrants, handles a zero x without dividing, and returns 0 rather than NaN when both arguments are 0 — all cases where Math.atan(y / x) loses information or fails.
More examples
Compass bearings from 0 to 360
Quadrants three and four come back negative from atan2 — the raw -135 becomes 225 after normalization, and -45 becomes 315. The double modulo handles negatives correctly where a single % would leave them negative.
function bearing(dx, dy) {
const deg = (Math.atan2(dy, dx) * 180) / Math.PI;
return ((deg % 360) + 360) % 360;
}
console.log(bearing(1, 1));
console.log(bearing(-1, 1));
console.log(bearing(-1, -1)); // raw value is -135
console.log(bearing(1, -1));
console.log(bearing(0, 0));Cartesian to polar and back
The point (3,4) has radius 5 at 53.13 degrees, and the round trip returns 3.0000000000000004 and 3.9999999999999996 — one rounding step of drift in each component. The opposite point (-3,-4) gets -2.2143 radians, a genuinely different angle that Math.atan(y/x) would have collapsed onto the same value.
function toPolar(x, y) {
return { r: Math.hypot(x, y), theta: Math.atan2(y, x) };
}
function toCartesian({ r, theta }) {
return { x: r * Math.cos(theta), y: r * Math.sin(theta) };
}
const p = toPolar(3, 4);
console.log(p.r, ((p.theta * 180) / Math.PI).toFixed(2) + "deg");
console.log(toCartesian(p));
console.log(toPolar(-3, -4).theta.toFixed(4)); // opposite directionScreen coordinates point downward
The target is 50 pixels above the origin on screen, but its dy is negative, so the raw call reports -90. Negating y for a downward axis gives the intuitive 90. The third line shows the argument-order bug: swapping produces 180 instead of -90.
const deg = (r) => Math.round((r * 180) / Math.PI);
const target = { x: 100, y: 50 }, origin = { x: 100, y: 100 };
const dx = target.x - origin.x, dy = target.y - origin.y;
console.log("raw:", deg(Math.atan2(dy, dx)));
console.log("screen up:", deg(Math.atan2(-dy, dx)));
console.log("swapped args:", deg(Math.atan2(dx, dy)));
Discussion