Detect Clicks on Matter.js Bodies Using MouseConstraint

This article explains how to detect user clicks on physics bodies in Matter.js using the MouseConstraint module. By attaching event listeners to the mouse constraint and checking for an active body reference, you can reliably capture click events and execute custom logic whenever a user interacts with an object in your 2D physics scene.

Setting Up Mouse and MouseConstraint

To capture mouse interactions, Matter.js requires both a Mouse instance and a MouseConstraint. The Mouse tracks screen coordinates relative to the render canvas, while the MouseConstraint connects mouse interactions to the physics engine.

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

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

// Create mouse and mouse constraint
const mouse = Mouse.create(render.canvas);
const mouseConstraint = MouseConstraint.create(engine, {
    mouse: mouse,
    constraint: {
        stiffness: 0.2,
        render: { visible: false }
    }
});

Composite.add(engine.world, mouseConstraint);
render.mouse = mouse;

Listening for Click Events

Matter.js provides an Events module to listen for interactions on the MouseConstraint. The most direct way to detect a click is by listening for the mousedown event on the mouseConstraint instance.

When mousedown fires, inspect mouseConstraint.body. If the mouse cursor is hovering over a body when clicked, mouseConstraint.body will reference that specific body; otherwise, it will be null.

Events.on(mouseConstraint, 'mousedown', function(event) {
    const clickedBody = mouseConstraint.body;

    if (clickedBody) {
        console.log('Body clicked:', clickedBody);
        // Execute custom logic here (e.g., change color, apply force, remove body)
    }
});

Differentiating Clicks from Drags

The mousedown event fires immediately upon pressing the mouse button. If you need to detect a full click (a press followed by a release) without triggering on drag gestures, track the mouse state between mousedown and mouseup:

let targetBody = null;

Events.on(mouseConstraint, 'mousedown', function() {
    targetBody = mouseConstraint.body;
});

Events.on(mouseConstraint, 'mouseup', function() {
    if (targetBody && targetBody === mouseConstraint.body) {
        console.log('Body clicked without dragging away:', targetBody);
    }
    targetBody = null;
});

Filtering Specific Bodies

To limit interactions to specific entities, assign custom properties or labels to your bodies during creation, then evaluate those labels inside the event handler:

const interactiveBox = Bodies.rectangle(400, 200, 80, 80, {
    label: 'clickableBox'
});
Composite.add(engine.world, interactiveBox);

Events.on(mouseConstraint, 'mousedown', function() {
    const body = mouseConstraint.body;
    if (body && body.label === 'clickableBox') {
        // Specific logic for this body
        Matter.Body.scale(body, 1.1, 1.1);
    }
});

Using mouseConstraint.body inside Matter.js Events listeners provides a fast and efficient way to handle clicks, raycasting, and user selections directly within your simulation loop.