The Shoelace Formula

Finding the center of a shape that doesn't want to be centered.

Slight disclaimer before reading: I don't have a math background. I pick up whatever I need when a project corners me into it.

I was building an index of real estate prices in Montenegro and needed to place a label at the visual center of each municipality.

Sounds simple to us humans, we can just eyeball it, but how do you tell the computer to find the center programmatically, without hardcoding it as a screen position. Especially as municipalities can have shapes which are very much so, irregular.

Let's take Budva as an example, its shape is a concave polygon, reflecting its coastal and mountainous borders.

The naive approaches either land the label outside the polygon entirely or somewhere misleading.

Budva municipality, 66 vertices wrapping the area of Budva. Where should the label go?

What does "center" even mean for an irregular, concave polygon? And how do you compute it?

BTW, I know: Turf.js, D3, or any GIS library can do this in one line. This was me wanting to understand what's actually happening under the hood.

Start simple: rectangles and triangles


For a rectangle, the center is obvious. Average the corners. For a triangle, same thing.

This is the vertex average: the arithmetic mean of all vertex coordinates.

cx = (x₀ + x₁ + x₂ + ... + xₙ) / n
cy = (y₀ + y₁ + y₂ + ... + yₙ) / n
Vertex average on a rectangle and triangle. The dot lands exactly where you would expect.

This works. But it only works because these shapes are simple and, most importantly, convex. Convex being the opposite of concave, the opposite of "dented", you could say simply.

Convex polygons: the vertex average still works


A convex polygon is one where every interior angle is less than 180 degrees. No dents. For any convex polygon (pentagon, hexagon, whatever), the vertex average lands inside the shape and looks reasonable.

Vertex average vs Shoelace on a convex hexagon. Nearly identical.

So when does this break?

Let's say a polygon is convex but elongated, with lots of vertices clustered along a thin section that covers very little area.

All those vertices pile into the average and drag it toward the thin section, way off from where we'd eyeball the center. Not all vertices should be weighted equally. Let's fix that.

Fan triangulation: weighting by area


Instead of treating all vertices equally, decompose the polygon into triangles and weight each triangle's center by its area. This is fan triangulation. Pick one vertex, draw lines to all the others. You get a fan of triangles.

Fan triangulation of a convex hexagon. Each triangle's centroid is weighted by its area.

The formula is a weighted average of triangle centroids:

centroid = Σ(areaᵢ × centroidᵢ) / Σ(areaᵢ)

This accounts for how much space each part of the polygon actually occupies. For convex polygons with simple shapes, it gives almost the same answer as the vertex average.

For convex polygons with elongated sections, it pulls the center to where most of the "mass" is, better than vertex average.

But, the real test are concave shapes.

Concave polygons: fan triangulation breaks


A concave polygon has at least one interior angle greater than 180 degrees. A dent. Fan triangulation treats every triangle's area as positive. So when a triangle covers space outside the polygon (which happens at the dent), that exterior area gets added to the total instead of subtracted. The centroid shifts toward area that isn't part of the shape.

Fan triangulation on an L-shape. Red triangles cover the concavity. The computed center gets pulled into the void.

We need an algorithm that is aware of the polygon's actual area distribution, the parts that are "missing" in concave shapes included.

The Shoelace Formula


It starts with the 2D cross product.
Two vectors u = (a, b) and v = (c, d) span a parallelogram with area |ad - bc|.
A triangle is half that parallelogram: ½|ad - bc|.

Two multiplications, one subtraction. No trig, no height measurements.

Two vectors span a parallelogram. The triangle is exactly half. The area of each is computed from the 2D cross product: ad − bc.

The Shoelace formula (Meister, 1769) applies this triangle trick to every edge of the polygon. Each edge, together with the origin, forms a triangular wedge. Walk the boundary edge by edge: wedges swept counter-clockwise contribute positive area, clockwise wedges contribute negative. The exterior wedges cancel out and you are left with only the polygon's true interior. Sometimes called the surveyor's walk.

The origin does not even need to be inside the polygon. The positive and negative wedges always cancel to the correct result regardless of where it sits. For concave polygons, some wedges subtract the "missing" regions.

Surveyor's walk
CCW (+)CW ()
Step 0 of 6
Each edge, together with the origin, forms a triangular wedge. Walk the boundary and watch the signs.
Step through each edge. Green wedges add area, red wedges subtract it.

The name comes from the multiplication pattern. Each term multiplies diagonally: xᵢ with yᵢ₊₁ going forward, xᵢ₊₁ with yᵢ going backward. It looks like lacing a shoe.

The diagonal multiplication pattern that gives the formula its name. Forward diagonals subtract backward diagonals, like lacing a shoe.

Signed area

The formula iterates through consecutive vertex pairs and computes a cross product for each edge:

cross = xᵢ · yᵢ₊₁ − xᵢ₊₁ · yᵢ

Sum all cross products, divide by 2. That gives the signed area. Positive if the vertices are counter-clockwise, negative if clockwise.

let area = 0;
for (let i = 0; i < n; i++) {
  const j = (i + 1) % n; // wraps last vertex back to first
  area += pts[i].x * pts[j].y - pts[j].x * pts[i].y;
}
area /= 2;

The % n (modulo) closes the polygon: when i reaches the last vertex (n - 1), j wraps to 0, pairing the last vertex with the first. Without it, we'd miss the closing edge.

Weighted centroid

Same loop, same cross products, but now each one also accumulates the coordinate sum of the edge's two endpoints. Divide by 6A at the end: 2 from the area formula, 3 because a triangle's centroid sits at one-third the sum of its vertices.

let cx = 0, cy = 0, area = 0;
for (let i = 0; i < n; i++) {
  const j = (i + 1) % n; // wraps last vertex back to first
  const cross = pts[i].x * pts[j].y - pts[j].x * pts[i].y;
  area += cross;
  cx += (pts[i].x + pts[j].x) * cross;
  cy += (pts[i].y + pts[j].y) * cross;
}
area /= 2;
const centroid = { x: cx / (6 * area), y: cy / (6 * area) };

Watch it work


Step through the algorithm on both a convex and concave shape. Cross products can be positive or negative depending on how each edge sweeps relative to the origin. On the convex shape, they all cancel out cleanly into the right area. On the concave shape, the void region gets subtracted out, which is why the Shoelace centroid lands inside the shape while a simple vertex average doesn't.

Shoelace — step by step
Step 0 of 6
A convex pentagon with 5 vertices. Each edge computes xᵢ·yᵢ₊₁ − xᵢ₊₁·yᵢ. Click Next.
Click through each edge. Watch the cross products accumulate.

Back to Budva


Back to the opening problem. Run all three methods on Budva's real 66-vertex coastline. Switch between tabs and watch each algorithm work step by step.

Budva — run each method

The difference is structural. Fan triangulation from a single vertex creates triangles that spill outside the polygon on concave shapes, and if you just sum those up, your centroid includes area that isn't part of the shape.

The Shoelace formula fans from the origin instead. Its cross products carry sign: counter-clockwise wedges add, clockwise wedges subtract. The exterior cancels itself out automatically.

Beyond maps


The same formula shows up whenever you need the area or centroid of an arbitrary polygon: computational geometry, physics (center of mass), computer graphics (polygon fill), CAD.