delaunator vs earcut vs polylabel vs clipper-lib
Computational Geometry Primitives for Frontend Visualization
delaunatorearcutpolylabelclipper-libSimilar Packages:

Computational Geometry Primitives for Frontend Visualization

clipper-lib, delaunator, earcut, and polylabel are specialized JavaScript libraries for handling computational geometry tasks in web applications. clipper-lib performs boolean operations on polygons like union and intersection. delaunator generates Delaunay triangulations from point sets, useful for mesh generation. earcut triangulates polygons with holes for efficient WebGL rendering. polylabel finds the optimal visual center of a polygon for placing labels. Together, they cover the core needs of mapping, data visualization, and interactive graphics.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
delaunator16,626,8952,60466.9 kB53 months agoISC
earcut9,830,1772,543108 kB011 hours agoISC
polylabel401,3441,54516.3 kB117 days agoISC
clipper-lib50,074212215 kB7-BSL

Computational Geometry Primitives for Frontend Visualization

When building mapping tools, data visualizations, or interactive graphics, you often need to manipulate shapes, meshes, and labels. The packages clipper-lib, delaunator, earcut, and polylabel are industry-standard utilities for these specific geometric tasks. They are not general-purpose graphics libraries but rather focused engines that solve hard math problems so you don't have to.

Let's break down their specific roles, how they handle data, and where they fit in your architecture.

✂️ Polygon Boolean Operations: clipper-lib

clipper-lib is a JavaScript port of the famous Clipper library. It handles boolean operations on polygons. This means you can take two shapes and find their union, difference, intersection, or XOR.

This is essential for features like "brushing" on a map, combining geographic regions, or creating complex masks.

import ClipperLib from 'clipper-lib';

const subject = [{ X: 100, Y: 100 }, { X: 200, Y: 100 }, { X: 200, Y: 200 }, { X: 100, Y: 200 }];
const clip = [{ X: 150, Y: 150 }, { X: 250, Y: 150 }, { X: 250, Y: 250 }, { X: 150, Y: 250 }];

const clipper = new ClipperLib.Clipper();
clipper.AddPath(subject, ClipperLib.PolyType.ptSubject, true);
clipper.AddPath(clip, ClipperLib.PolyType.ptClip, true);

const solution = new ClipperLib.Paths();
clipper.Execute(ClipperLib.ClipType.ctIntersection, solution);
// solution contains the overlapping area

Key Constraint: clipper-lib works with integers, not floats. You must multiply your coordinates by a scaling factor (e.g., 1000) before passing them in, then divide the results by the same factor. This avoids floating-point errors during calculation.

🔺 Triangulation: earcut vs delaunator

Triangulation is the process of breaking shapes into triangles. Both earcut and delaunator do this, but they start from different inputs.

earcut: Polygons to Triangles

earcut takes a defined polygon (with optional holes) and breaks it into triangles. This is exactly what WebGL needs to render a shape. If you have a GeoJSON polygon and want to draw it on a canvas, this is your tool.

import earcut from 'earcut';

// Flat array of x,y coordinates
const vertices = [0, 0, 100, 0, 100, 100, 0, 100];
const holes = [];
const dimensions = 2;

const indices = earcut(vertices, holes, dimensions);
// indices: [0, 1, 2, 0, 2, 3] (ready for WebGL buffer)

delaunator: Points to Mesh

delaunator takes a cloud of points and connects them into a mesh. It does not care about polygon boundaries initially. It creates a Delaunay triangulation, which maximizes the minimum angle of all triangles. This is great for terrain generation or connecting scatter plot points.

import Delaunator from 'delaunator';

// Array of [x, y] points
const points = [[0, 0], [100, 0], [50, 50], [0, 100]];

const delaunay = new Delaunator(points);
const triangles = delaunay.triangles;
// Uint32Array of indices connecting the points

Trade-off: Use earcut when you have a boundary (a shape). Use delaunator when you have a set of points and want to find the structure between them.

🏷️ Label Placement: polylabel

polylabel solves a specific visual problem: where do I put the text "California" inside the shape of California? The geometric centroid often falls outside the shape or in a narrow arm. polylabel finds the "pole of inaccessibility" — the point furthest from any edge.

import polylabel from 'polylabel';

// Polygon as array of rings (first is outer, rest are holes)
const polygon = [
  [[0, 0], [100, 0], [100, 100], [0, 100]] // Simple square
];

const [x, y] = polylabel(polygon, 1.0);
// Returns the best [x, y] for a label

It uses an iterative grid search to find the optimal spot. You can adjust the precision parameter to balance speed and accuracy.

⚡ Performance and Data Structures

These libraries are designed for speed, but they handle memory differently.

  • earcut and delaunator return typed arrays (Uint32Array). This is crucial for WebGL. You can pass these directly to GPU buffers without copying data. This makes them extremely fast for rendering thousands of features.
  • clipper-lib uses JavaScript objects for points ({ X: 1, Y: 1 }). This creates more garbage collection pressure. For heavy-duty clipping in a loop, you may need to manage object reuse or consider a WASM alternative if performance lags.
  • polylabel returns a simple array [x, y]. It is computationally heavier than triangulation because it searches iteratively. Use it sparingly — ideally once per polygon during data preparation, not every frame.

🌐 Real-World Integration Patterns

Scenario 1: Interactive Map Drawing

You let users draw shapes that merge together.

  • Best choice: clipper-lib
  • Why? You need to calculate the union of the existing shape and the new brush stroke.
// Merge existing region with new stroke
const merged = performClip(existingPaths, newStrokePaths, 'union');

Scenario 2: Vector Tile Rendering

You are building a map renderer like Mapbox GL.

  • Best choice: earcut
  • Why? You receive polygon coordinates from the server and need to triangulate them for the GPU.
// Prepare geometry for WebGL
const vertices = flatten(polygonCoords);
const indices = earcut(vertices);
webglBuffer.setData(indices);

Scenario 3: Scatter Plot Visualization

You have 10,000 data points and want to show density regions.

  • Best choice: delaunator
  • Why? You can generate a mesh from the points and color triangles based on density.
// Create mesh from scattered data
const mesh = new Delaunator(dataPoints);
renderTriangles(mesh.triangles);

Scenario 4: Choropleth Map Labels

You have a map of states and need to place names inside each state.

  • Best choice: polylabel
  • Why? Centroids often land in water or outside borders for irregular states.
// Find safe label position
const labelPos = polylabel(statePolygon);
placeText(labelPos[0], labelPos[1], stateName);

📊 Summary Table

Featureclipper-libdelaunatorearcutpolylabel
Primary InputPaths (Polygons)Points (Cloud)Vertices (Polygon)Polygon (Rings)
Primary OutputClipped PathsTriangulation MeshTriangle IndicesSingle Point [x,y]
Coordinate TypeIntegers (Scaled)FloatsFloatsFloats
Handles Holes✅ Yes❌ No✅ Yes✅ Yes
Use CaseBoolean OpsMesh GenerationRenderingLabel Placement

💡 Final Recommendation

These tools are complementary, not competitive. A complex visualization app might use all four.

  1. Use clipper-lib to prepare and modify your geometry data (e.g., cutting a map view to a specific region).
  2. Use earcut to convert that prepared geometry into triangles for the GPU.
  3. Use delaunator if you are visualizing point data rather than predefined shapes.
  4. Use polylabel during the data loading phase to calculate where labels should go before rendering starts.

Note on Maintenance: delaunator, earcut, and polylabel are maintained by Mapbox and are highly stable. clipper-lib is a community port of a C++ library. It is stable but receives fewer updates. For new projects requiring heavy clipping performance, evaluate if a WebAssembly port fits your needs better, but clipper-lib remains the standard for pure JavaScript environments.

How to Choose: delaunator vs earcut vs polylabel vs clipper-lib

  • delaunator:

    Choose delaunator when you have a cloud of points and need to generate a mesh or Voronoi diagram from them. It is extremely fast and ideal for procedural generation or connecting scattered data points. It does not handle polygon holes or boolean logic, only point-based triangulation.

  • earcut:

    Choose earcut when you need to render complex polygons with holes in WebGL or Canvas. It converts polygon definitions into triangle indices that GPUs can draw efficiently. It is the go-to solution for map rendering and vector tile processing where performance is critical.

  • polylabel:

    Choose polylabel when you need to place a text label or icon inside an irregular polygon without it overlapping the edges. It calculates the 'pole of inaccessibility', ensuring the label sits in the most spacious part of the shape. It is specifically designed for cartography and label placement.

  • clipper-lib:

    Choose clipper-lib when you need to perform boolean operations on polygons, such as merging shapes, cutting holes, or calculating intersections. It is the standard choice for 2D polygon clipping in pure JavaScript. Be aware that it uses integer coordinates, so you must scale your data before processing and scale it back afterward.

README for delaunator

Delaunator CI

An incredibly fast and robust JavaScript library for Delaunay triangulation of 2D points.

Delaunay triangulation example

Projects based on Delaunator

  • d3-delaunay for Voronoi diagrams, search, traversal and rendering (a part of D3).
  • d3-geo-voronoi for Delaunay triangulations and Voronoi diagrams on a sphere (e.g. for geographic locations).

Example

const coords = [377,479,  453,434,  326,387,  444,359,  511,389,
                586,429,  470,315,  622,493,  627,367,  570,314];
const delaunay = new Delaunator(coords);
console.log(delaunay.triangles);
// [4,3,1,  4,6,3,  1,5,4,  4,9,6,  2,0,1,  1,7,5,
//  5,9,4,  6,2,3,  3,2,1,  5,8,9,  0,7,1,  5,7,8]
Delaunay triangulation example with labeled points

Install

Install with NPM (npm install delaunator) or Yarn (yarn add delaunator), then import as an ES module:

import Delaunator from 'delaunator';

To use as a module in a browser:

<script type="module">
    import Delaunator from 'https://cdn.skypack.dev/delaunator@5.0.0';
</script>

Or use a browser UMD build that exposes a Delaunator global variable:

<script src="https://unpkg.com/delaunator@5.0.0/delaunator.min.js"></script>

API Reference

new Delaunator(coords)

Constructs a delaunay triangulation object given an array of point coordinates of the form: [x0, y0, x1, y1, ...] (use a typed array for best performance).

Delaunator.from(points[, getX, getY])

Constructs a delaunay triangulation object given an array of points ([x, y] by default). getX and getY are optional functions of the form (point) => value for custom point formats. Duplicate points are skipped.

delaunay.triangles

A Uint32Array array of triangle vertex indices (each group of three numbers forms a triangle). All triangles are directed counterclockwise.

To get the coordinates of all triangles when using Delaunator.from(points), use:

for (let i = 0; i < triangles.length; i += 3) {
    coordinates.push([
        points[triangles[i]],
        points[triangles[i + 1]],
        points[triangles[i + 2]]
    ]);
}

To get the coordinates of all triangles when using new Delaunator(coords), use:

for (let i = 0; i < triangles.length; i += 3) {
    coordinates.push([
        [coords[2 * triangles[i]],     coords[2 * triangles[i] + 1]],
        [coords[2 * triangles[i + 1]], coords[2 * triangles[i + 1] + 1]],
        [coords[2 * triangles[i + 2]], coords[2 * triangles[i + 2] + 1]]
    ]);
}

delaunay.halfedges

A Int32Array array of triangle half-edge indices that allows you to traverse the triangulation. i-th half-edge in the array corresponds to vertex triangles[i] the half-edge is coming from. halfedges[i] is the index of a twin half-edge in an adjacent triangle (or -1 for outer half-edges on the convex hull).

The flat array-based data structures might be counterintuitive, but they're one of the key reasons this library is fast.

delaunay.hull

A Uint32Array array of indices that reference points on the convex hull of the input data, counter-clockwise.

delaunay.coords

An array of input coordinates in the form [x0, y0, x1, y1, ....], of the type provided in the constructor (or Float64Array if you used Delaunator.from).

delaunay.update()

Updates the triangulation if you modified delaunay.coords values in place, avoiding expensive memory allocations. Useful for iterative relaxation algorithms such as Lloyd's.

Performance

Benchmark results against other Delaunay JS libraries (npm run bench on Macbook Pro Retina 15" 2017, Node v10.10.0):

 uniform 100kgauss 100kgrid 100kdegen 100kuniform 1 milliongauss 1 milliongrid 1 milliondegen 1 million
delaunator82ms61ms66ms25ms1.07s950ms830ms278ms
faster‑delaunay473ms411ms272ms68ms4.27s4.62s4.3s810ms
incremental‑delaunay547ms505ms172ms528ms5.9s6.08s2.11s6.09s
d3‑voronoi972ms909ms358ms720ms15.04s13.86s5.55s11.13s
delaunay‑fast3.8s4s12.57stimeout132s138s399stimeout
delaunay4.85s5.73s15.05stimeout156s178s326stimeout
delaunay‑triangulate2.24s2.04sOOM1.51sOOMOOMOOMOOM
cdt2d45s51s118s17stimeouttimeouttimeouttimeout

Papers

The algorithm is based on ideas from the following papers:

Robustness

Delaunator should produce valid output even on highly degenerate input. It does so by depending on robust-predicates, a modern port of Jonathan Shewchuk's robust geometric predicates, an industry standard in computational geometry.

Ports to other languages