DEV Community

levent çelik
levent çelik

Posted on

Five geometry formulas every JavaScript developer ends up rewriting

I have lost count of how many times I have re-implemented the distance between two points in a side project. Every map, every drag-and-drop, every collision check, every "snap to nearest" feature ends up needing a small bag of geometry helpers. Here are the five I keep reaching for, with the code I actually paste into utils files, and a few notes on the gotchas that bit me.

1. Distance between two points

The Pythagorean theorem dressed up as a one-liner:

function distance(a, b) {
  const dx = a.x - b.x;
  const dy = a.y - b.y;
  return Math.hypot(dx, dy);
}
Enter fullscreen mode Exit fullscreen mode

Math.hypot is underused. It is the idiomatic way to do sqrt(dx*dx + dy*dy) in JavaScript and it handles overflow and underflow correctly when your numbers are huge or tiny. If you ever wrote Math.sqrt(dx*dx + dy*dy) in production, swap it now.

When I want to double-check my mental model on a specific triangle (especially when working with screen-space sizes), I open the Pythagorean Theorem Calculator on Equation Solver and plug in the two legs. Faster than printing from a REPL.

2. Triangle area from three points

The shoelace formula collapses beautifully for a single triangle:

function triangleArea(a, b, c) {
  return Math.abs(
    (a.x * (b.y - c.y) +
     b.x * (c.y - a.y) +
     c.x * (a.y - b.y)) / 2
  );
}
Enter fullscreen mode Exit fullscreen mode

No need for base-times-height when you already have coordinates. If you drop the Math.abs, the sign even tells you the orientation (clockwise or counter-clockwise), which is a free win for things like detecting inside/outside.

For a sanity check with side lengths instead of coordinates, the Triangle Calculator on Equation Solver supports both Heron's formula and base/height, which is convenient when you are translating a problem from a textbook.

3. Polygon area for arbitrary closed shapes

The full shoelace formula generalizes the previous one to any simple polygon, given as an array of points in order:

function polygonArea(points) {
  let sum = 0;
  for (let i = 0; i < points.length; i++) {
    const a = points[i];
    const b = points[(i + 1) % points.length];
    sum += a.x * b.y - b.x * a.y;
  }
  return Math.abs(sum) / 2;
}
Enter fullscreen mode Exit fullscreen mode

Three gotchas I have hit personally: (1) the polygon must be simple, no self-intersections; (2) the points must be ordered, either all clockwise or all counter-clockwise; (3) the result is in whatever units your coordinates are squared, so mixing pixels and meters silently is painful.

When I am verifying a tricky shape against a reference, I paste the vertices into the Polygon Area Calculator on Equation Solver and compare. It also draws the polygon, which is genuinely helpful when I am trying to spot whether my array is in the wrong winding order.

4. Point inside a circle

Classic, but I still see square roots in code reviews where they are not needed:

function insideCircle(point, center, radius) {
  const dx = point.x - center.x;
  const dy = point.y - center.y;
  return dx * dx + dy * dy <= radius * radius;
}
Enter fullscreen mode Exit fullscreen mode

Never take the square root just to compare against a radius. Squaring both sides is faster, has no precision loss from Math.sqrt, and is exactly what hot-path code in games and physics engines does.

For area and circumference of a given radius, the Circle Calculator on Equation Solver is the page I keep open when I am sizing canvases and viewports.

5. Rectangle overlap

Separating Axis Theorem for the simplest case, axis-aligned rectangles:

function rectsOverlap(a, b) {
  return (
    a.x < b.x + b.w &&
    a.x + a.w > b.x &&
    a.y < b.y + b.h &&
    a.y + a.h > b.y
  );
}
Enter fullscreen mode Exit fullscreen mode

It looks asymmetric, but every comparison there is necessary; drop one and you get false positives or negatives at edges. I have a small unit test that hits the four "just touching" cases, because off-by-one on collision checks shows up as the most embarrassing kind of bug: "the bullet went through the wall."

If you need a refresher on rectangle area and perimeter for sizing layouts, the Rectangle Calculator on Equation Solver is a one-screen reference.

A small style note

I used to put all of these on a Geometry class. I do not anymore. Plain functions composed at the call site are easier to tree-shake, easier to test, and easier to read out of context. Geometry is a great example of "functions over classes" in practice; there is no state to hide.

If there is a sixth one I would add, it is a good clamp(value, min, max) helper. Half of the geometry code I write ends up wanting to clamp something to a range, and writing Math.min(Math.max(...)) for the hundredth time is a sign your toolbox is missing a tool.

Top comments (0)