Decomposing Complex SVG Paths in Matter.js
This guide explains how to convert complex, concave SVG paths into
functional rigid bodies in Matter.js using automated convex
decomposition. Because 2D physics engines struggle with concave
collision detection, complex SVG paths must be converted into coordinate
samples and broken down into sets of convex sub-polygons. By integrating
the poly-decomp library with Matter.js's native vertex and
SVG modules, you can dynamically transform raw SVG path strings into
optimized, multi-part compound bodies directly within your
simulation.
1. Enable the Convex Decomposition Engine
Matter.js relies on an external library called
poly-decomp to execute the Bayazit or Greene decomposition
algorithms. You must provide poly-decomp to the window
object or inject it directly into Matter.js before creating bodies from
non-convex vertices.
import Matter from 'matter-js';
import decomp from 'poly-decomp';
// Attach poly-decomp to Matter.js
Matter.Common.setDecomp(decomp);If using browser <script> tags, ensure
poly-decomp.js is loaded before matter.js,
which allows Matter.js to detect it automatically on
window.decomp.
2. Extract Vertices from SVG Path Data
To convert an SVG path string into coordinate data that Matter.js can
interpret, dynamically create an SVG path element in memory and pass it
to Matter.Svg.pathToVertices.
function parseSVGPathToVertices(pathData, sampleResolution = 15) {
// Create an in-memory SVG path element
const pathElement = document.createElementNS('http://www.w3.org/2000/svg', 'path');
pathElement.setAttribute('d', pathData);
// Sample points along the SVG path
// Lower sampleResolution means fewer points and better performance
return Matter.Svg.pathToVertices(pathElement, sampleResolution);
}sampleResolution determines the distance interval
between sampled points along the curve. Lowering this value creates
smoother boundaries at the cost of generating more vertices and
increasing collision calculation overhead.
3. Generate the Convex Decomposed Rigid Body
Pass the generated vertex list to
Matter.Bodies.fromVertices(). Matter.js uses the injected
decomposition library to automatically divide concave vertex loops into
a collection of convex collision hulls, binding them into a single
compound body.
function createBodyFromSVG(x, y, pathData, options = {}) {
const vertices = parseSVGPathToVertices(pathData);
// Automatically decompose concave loops into a compound convex body
const body = Matter.Bodies.fromVertices(x, y, vertices, options, true);
return body;
}Passing true as the fifth argument
(flagInternal) marks the internal edges created by the
decomposition as non-colliding, preventing neighboring convex parts of
the same body from snagging on edges during collisions.
4. Correct for Center of Mass Offsets
When Matter.Bodies.fromVertices calculates a compound
body, it computes the center of mass based on the geometry and
automatically resets the origin to that point. This shifts the internal
body position away from the initial (x, y) target
coordinates.
To position the body accurately in relation to your original SVG canvas coordinates:
function createAlignedBodyFromSVG(x, y, pathData, options = {}) {
const vertices = parseSVGPathToVertices(pathData);
const body = Matter.Bodies.fromVertices(x, y, vertices, options, true);
// Compute the offset between the geometric bounds and the calculated center of mass
const bounds = Matter.Bounds.create(vertices);
const width = bounds.max.x - bounds.min.x;
const height = bounds.max.y - bounds.min.y;
// Set the body precisely at the intended coordinates relative to its top-left bound
Matter.Body.setPosition(body, {
x: x + width / 2,
y: y + height / 2
});
return body;
}Complete Dynamic Implementation
import Matter from 'matter-js';
import decomp from 'poly-decomp';
Matter.Common.setDecomp(decomp);
const { Engine, Render, Runner, Composite, Bodies, Svg } = Matter;
const engine = Engine.create();
const world = engine.world;
const starSVGPath = "M 50,5 L 64,36 L 98,36 L 70,57 L 81,91 L 50,70 L 19,91 L 30,57 L 2,36 L 36,36 Z";
function createDecomposedSVG(pathString, x, y, options = {}) {
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
path.setAttribute('d', pathString);
const vertices = Svg.pathToVertices(path, 10);
return Bodies.fromVertices(x, y, vertices, {
isStatic: false,
restitution: 0.5,
...options
}, true);
}
// Add decomposed SVG body to the simulation
const dynamicBody = createDecomposedSVG(starSVGPath, 400, 100);
Composite.add(world, dynamicBody);