Use Path2D to Draw Custom Matter.js Bodies
This article explains how to render custom visual shapes over Matter.js physics bodies using the HTML5 Canvas Path2D API. By default, Matter.js renders bodies as basic geometric wireframes or flat polygons using its built-in renderer. By disabling standard body rendering and tapping into the render loop, you can map complex SVG-style paths directly to the transformation matrix of moving rigid bodies.
Step 1: Initialize Matter.js Without Default Body Rendering
To draw custom graphics with Path2D, create your physics
engine and renderer, but disable standard rendering for the specific
body by setting render.visible to false.
const { Engine, Render, Runner, Bodies, Composite, Events } = Matter;
const engine = Engine.create();
const canvas = document.getElementById('world');
const ctx = canvas.getContext('2d');
const render = Render.create({
canvas: canvas,
engine: engine,
options: {
width: 800,
height: 600,
wireframes: false,
background: '#f0f0f0'
}
});
Render.run(render);
Runner.run(Runner.create(), engine);Step 2: Define the Physics Body and the Path2D Object
Create a physical body that roughly matches the dimensions of your
custom path. Define the Path2D object using standard SVG
path syntax or standard canvas path commands. Ensure the origin
(0, 0) of your Path2D path corresponds to the
center of mass of the physics body.
// Define the Path2D shape centered around (0, 0)
const heartSvgPath = "M 0 -20 A 15 15 0 0 0 -30 -20 A 15 15 0 0 0 0 20 A 15 15 0 0 0 30 -20 A 15 15 0 0 0 0 -20";
const customPath = new Path2D(heartSvgPath);
// Create a corresponding physics body
const customBody = Bodies.circle(400, 100, 25, {
restitution: 0.8,
render: {
visible: false // Prevent default rendering
}
});
// Add boundaries to catch the body
const ground = Bodies.rectangle(400, 580, 810, 40, { isStatic: true });
Composite.add(engine.world, [customBody, ground]);Step 3: Synchronize Path2D with the Matter.js Render Loop
Listen to the afterRender event on the Matter.js
Render instance. For every frame, save the canvas context
state, translate and rotate the canvas context to match the body's
coordinates and rotation angle, and draw the Path2D
object.
Events.on(render, 'afterRender', () => {
ctx.save();
// Translate context to body position
ctx.translate(customBody.position.x, customBody.position.y);
// Rotate context to body angle
ctx.rotate(customBody.angle);
// Render the custom Path2D shape
ctx.fillStyle = '#e74c3c';
ctx.fill(customPath);
ctx.lineWidth = 2;
ctx.strokeStyle = '#c0392b';
ctx.stroke(customPath);
ctx.restore();
});Step 4: Aligning Complex Geometry
If your visual path does not match a basic primitive like a circle or
rectangle, use Matter.Svg.pathToVertices() from the
Matter.js plugins alongside poly-decomp to generate an exact physical
collision hull from the same SVG path string used in your
Path2D definition. When doing this, ensure the scale of the
SVG vertices used for collision matches the scaling applied to the
Path2D drawing commands.