Tutorials
April 30, 2026
javascripttypescriptbooleanstutorialwebassembly

Fast Mesh Booleans in JavaScript

Learn how to perform fast mesh boolean operations in JavaScript and TypeScript. Union, intersection, and difference at interactive speed on million-polygon meshes. WebAssembly-powered, runs in Node.js and the browser.

Žiga Sajovic, Polydera

Also available in C++ and Python.

trueform is the fastest JavaScript mesh boolean library for real-world meshes — performing exact boolean union, intersection, and difference at interactive speed. WebAssembly-powered, it runs in Node.js and the browser with the same API. This tutorial covers the basics: loading meshes, running booleans, and working with results. It then shows how to precompute structures and use shallow copies to avoid rebuilding them, enabling boolean operations on moving geometry at real-time rates.

trueform GitHub Documentation Try it live

We will use the Stanford Dragon to demonstrate boolean union, intersection, and difference between two offset copies of the same mesh.

Dragon
Dragon500K triangles
Union
UnionA ∪ B
Difference
DifferenceA \ B
Intersection
IntersectionA ∩ B

Each boolean under 27ms on 2×500K polygons via WebAssembly. Let's begin by installing trueform.

Install

npm install @polydera/trueform

The WebAssembly binary loads automatically on first use. In the browser, SharedArrayBuffer headers are required for multithreading — see the installation guide for details.

Loading the mesh

import * as tf from "@polydera/trueform";

Load from a file — in the browser via fetch, in Node.js via fs:

const response = await fetch("stanford_dragon.stl");
const bytes = new Uint8Array(await response.arrayBuffer());
const dragon = tf.readStl(bytes);

Or construct a mesh from flat arrays:

const faces = new Int32Array([0, 1, 2, 0, 2, 3]);
const points = new Float32Array([
    0,0,0, 1,0,0, 0,1,0, 1,1,0]);

const dragon = tf.mesh(faces, points);

The Mesh form wraps your data with geometric semantics — spatial queries, topology, and boolean operations all work directly on it.

Transformations

To perform a boolean between two copies of the dragon at different positions, set a transformation on the mesh. The underlying data is shared — no vertices are copied.

dragon.transformation = tf.makeTranslation(0, 1, 0);

Rotations and combined transforms:

dragon.transformation = tf.makeRotation(90, "z");
dragon.transformation = tf.makeRotation(45, [1, 0, 0], [0, 0, 5]);

The spatial tree and topology structures stay valid across transformations — they are applied on-the-fly during queries.

Boolean operations

With two forms ready, a boolean is a single call. Full documentation.

To boolean two copies at different positions, create a shallow copy. It shares all underlying data and structures but carries its own transformation:

const dragon = tf.readStl(bytes);

const translated = dragon.shallowCopy();
translated.transformation = tf.makeTranslation(0, 1, 0);

Union

const { mesh, labels, faceLabels } =
    await tf.async.booleanUnion(dragon, translated);

Difference

const { mesh, labels, faceLabels } =
    await tf.async.booleanDifference(dragon, translated);

Intersection

const { mesh, labels, faceLabels } =
    await tf.async.booleanIntersection(dragon, translated);

All heavy operations have async variants under tf.async.* — they run off the main thread, keeping the UI responsive. Synchronous versions (tf.booleanUnion, etc.) are also available.

Working with results

Every boolean returns three values. mesh is the output Mesh. labels marks each output face — 0 for faces originating from the first input, 1 from the second. faceLabels maps each output face back to the index of its source face in the input, enabling attribute transfer.

To split the result into separate meshes by label:

const { components } =
    await tf.async.splitIntoComponents(mesh, labels);

Write to STL or OBJ — the result is a buffer you can download or save:

const stlBuffer = tf.writeStl(mesh);
const blob = new Blob(
    [stlBuffer.toTypedArray()],
    { type: "model/stl" });

Precomputed structures

When running multiple booleans on the same geometry, build the spatial and topological structures once. They are computed lazily on first use, but explicit precomputation avoids paying the cost during the boolean itself:

const dragon = tf.readStl(bytes);

await tf.async.buildTree(dragon);
await tf.async.computeFaceMembership(dragon);
await tf.async.computeManifoldEdgeLink(dragon);

Now create shallow copies at different positions. Each copy shares the same data and structures — only the transformation differs:

for (let t = 0.1; t <= 1.0; t += 0.1) {
    const view = dragon.shallowCopy();
    view.transformation = tf.makeTranslation(0, t, 0);

    const { mesh, labels, faceLabels } =
        await tf.async.booleanDifference(dragon, view);
}

10 booleans on 2×500K polygon meshes in under 140ms total. Spatial and topological structures computed once.

Extracting intersection curves

Any boolean can also return the intersection boundary as polylines embedded on the surface. Pass returnCurves: true:

const { mesh, labels, faceLabels, curves } =
    await tf.async.booleanDifference(dragon, translated, {
        returnCurves: true
    });

// Access curve data
for (const path of curves.paths) {
    console.log(path.data); // vertex indices
}
const curvePoints = curves.points; // NDArrayFloat32 [V, 3]

If only the intersection curves are needed without the boolean itself:

const curves = tf.intersectionCurves(dragon, translated);

Three.js integration

trueform meshes can be converted to Three.js geometries and back. Transformations use row-major matrices — Three.js uses column-major:

// trueform → Three.js
const geo = new THREE.BufferGeometry();
geo.setIndex(new THREE.BufferAttribute(mesh.faces.data, 1));
geo.setAttribute("position",
    new THREE.BufferAttribute(mesh.points.data, 3));

// Three.js matrix → trueform (transpose)
mesh.transformation =
    tf.ndarray(threeMatrix4.elements, [4, 4]).T;

Performance and robustness

All operations run at interactive speed on million-polygon meshes.

12msIntersection curves2×500K polygons
27msBoolean union2×500K polygons
250msPolygon arrangements2×500K polygons

Apple M4 Max 16 threads Node WebAssembly

Real meshes are not ideal manifolds. trueform handles them directly — no preprocessing, no manifold requirement.

FeatureHandlingConvex polygonsNative — not limited to trianglesNon-manifold edgesHandled directlyInconsistent windingBayesian classificationSelf-intersecting inputResolved via polygon arrangementsCoplanar primitivesExact — aligned/opposing boundary classificationContour crossingsResolved via indirect predicates

trueform is a robust CGAL alternative — and the fastest mesh boolean library available — for exact mesh boolean operations in C++, Python, and TypeScript.

Full benchmarks and methodology · How the algorithm works

Cite as
@article{polydera:fast-mesh-booleans-in-javascript,
  title={Fast Mesh Booleans in JavaScript},
  author={Sajovic, {\v{Z}}iga, Polydera},
  year={2026},
  url={https://polydera.com/tutorials/fast-mesh-booleans-in-javascript},
  organization={Polydera}
}