Custom Canvas Graphics Using Matter.js afterRender

Matter.js provides a built-in 2D canvas renderer that displays rigid bodies, constraints, and collisions automatically, but complex games and simulations often require custom visuals like trails, health bars, or textured overlays. By listening to the afterRender event on the Render module, you can access the underlying HTML5 Canvas 2D context immediately after Matter.js finishes drawing the physics scene. This allows you to draw your own shapes, text, or sprites directly on top of the physics bodies without interfering with the simulation.

Accessing the Canvas Context

To draw custom elements, attach an event listener to your Render instance using Matter.Events.on(). Within the callback, retrieve the 2D rendering context from render.context.

const { Engine, Render, Runner, Bodies, Composite, Events } = Matter;

// Create engine and renderer
const engine = Engine.create();
const render = Render.create({
    element: document.body,
    engine: engine,
    options: {
        width: 800,
        height: 600,
        wireframes: false
    }
});

Render.run(render);
Runner.run(Runner.create(), engine);

// Listen to the afterRender event
Events.on(render, 'afterRender', () => {
    const ctx = render.context;
    // Custom drawing routines go here
});

Drawing Relative to Physics Bodies

When rendering elements that follow physical bodies—such as nameplates, health bars, or rotating indicators—reference the body's position and angle properties. Always use context.save() and context.restore() to prevent style and coordinate transformations from leaking into subsequent frames.

const circle = Bodies.circle(400, 300, 30, { restitution: 0.8 });
Composite.add(engine.world, circle);

Events.on(render, 'afterRender', () => {
    const ctx = render.context;

    ctx.save();
    
    // Translate and rotate to match the body's transform
    ctx.translate(circle.position.x, circle.position.y);
    ctx.rotate(circle.angle);

    // Draw a custom visual element centered on the body
    ctx.fillStyle = '#ff4757';
    ctx.fillRect(-15, -15, 30, 30);

    ctx.restore();

    // Draw an overlay above the body (without rotation)
    ctx.save();
    ctx.fillStyle = '#ffffff';
    ctx.font = '12px Arial';
    ctx.textAlign = 'center';
    ctx.fillText('Player 1', circle.position.x, circle.position.y - 45);
    ctx.restore();
});

Handling Renderer Bounds and Zoom

If you enable camera panning or zooming by modifying render.bounds, canvas coordinates will not map 1:1 to physics space. In this scenario, transform the canvas context by the camera offset before drawing, or compute screen-space coordinates by subtracting render.bounds.min.x and render.bounds.min.y from the target positions.