Terrain Carving for Matter.js Ground Bodies
This guide explains how to implement dynamic terrain carving and wormhole creation in Matter.js using 2D polygon clipping and vertex decomposition. Because Matter.js does not support native Constructive Solid Geometry (CSG) or concave dynamic collisions without polygon decomposition, carving through static bodies requires subtracting shapes mathematically and regenerating the physics bodies in the simulation world.
1. Required Dependencies
Matter.js cannot decompose complex concave shapes into convex collision hulls on its own. You must provide a polygon decomposition library and a 2D polygon clipping library:
- poly-decomp: Enables Matter.js to break concave
polygons into convex sub-parts via
Matter.Common.setDecomp(). - polygon-clipping or polybooljs: Handles the boolean difference operation to carve the wormhole out of the terrain polygon.
import Matter from 'matter-js';
import decomp from 'poly-decomp';
import polygonClipping from 'polygon-clipping';
Matter.Common.setDecomp(decomp);2. Representing Ground as Polygons
Instead of using basic primitive rectangles
(Matter.Bodies.rectangle), represent your ground as an
array of 2D coordinates defining a closed polygon.
// A simple ground block defined clockwise
let groundCoords = [
[[0, 200], [800, 200], [800, 600], [0, 600]]
];3. Generating the Carve Shape
To carve a wormhole, generate a circular polygon or tunnel path at the target coordinates. The polygon must be represented in the same coordinate space as the ground terrain.
function createCirclePolygon(centerX, centerY, radius, segments = 16) {
const points = [];
for (let i = 0; i < segments; i++) {
const angle = (i / segments) * Math.PI * 2;
points.push([
centerX + Math.cos(angle) * radius,
centerY + Math.sin(angle) * radius
]);
}
return [points]; // Format matching polygon-clipping input
}4. Performing Boolean Subtraction
Execute a boolean difference between the current terrain polygons and the carve shape. This removes the overlapping area, creating the hole or tunnel.
function carveHole(terrainRings, holeRings) {
// polygonClipping.difference returns a MultiPolygon structure
return polygonClipping.difference(terrainRings, holeRings);
}5. Rebuilding the Physics Body
Matter.js bodies cannot have their geometry modified dynamically in
place. You must remove the existing terrain body, generate new bodies
using Matter.Bodies.fromVertices(), and re-add them to the
Matter world.
let groundBody = null;
function updateGroundPhysics(world, multiPolygon) {
// 1. Remove the old ground body from the world
if (groundBody) {
Matter.Composite.remove(world, groundBody);
}
const bodies = [];
// 2. Iterate through resulting polygons
for (const polygon of multiPolygon) {
for (const ring of polygon) {
// Map coordinates to Matter.Vector objects
const vertices = ring.map(pt => ({ x: pt[0], y: pt[1] }));
// 3. Create a static compound body using decomposed vertices
const body = Matter.Bodies.fromVertices(
0, 0,
vertices,
{
isStatic: true,
render: { fillStyle: '#2e2e2e' }
},
true // Flag to automatically fix/normalize vertices
);
if (body) {
// Correct position offset caused by fromVertices center-of-mass calculation
Matter.Body.setPosition(body, {
x: body.position.x + (body.bounds.min.x < 0 ? Math.abs(body.bounds.min.x) : 0),
y: body.position.y + (body.bounds.min.y < 0 ? Math.abs(body.bounds.min.y) : 0)
});
bodies.push(body);
}
}
}
// 4. Group all parts into a single composite or compound body
groundBody = Matter.Body.create({
parts: bodies.flatMap(b => b.parts.slice(1)),
isStatic: true
});
Matter.Composite.add(world, groundBody);
}6. Performance Optimizations
- Chunking: Divide wide terrains into a grid of distinct static bodies. Only apply boolean operations to chunks that intersect with the bounding box of the carve shape.
- Vertex Simplification: Applying repeated carvings
increases the vertex count exponentially. Run an algorithm such as
Ramer-Douglas-Peucker (RDP) on the resulting coordinates to eliminate
redundant collinear points before passing them to
Bodies.fromVertices(). - Segment Caps: Restrict the number of segments on the carving tool (e.g., 12–16 vertices for circles). Higher segment counts dramatically slow down vertex decomposition.