Detect Clicks with Matter.Query.point in Matter.js
This article explains how to detect user clicks on physics bodies in
a Matter.js simulation using the built-in
Matter.Query.point method. You will learn how to capture
click coordinates from the canvas, query the physics world to find
intersecting bodies, and trigger custom logic based on the clicked
body.
What is Matter.Query.point?
Matter.Query.point is a spatial query function provided
by Matter.js. It tests a specific { x, y } coordinate
against an array of bodies to determine if the point lies inside any of
their collision shapes.
The syntax is:
Matter.Query.point(bodies, point)bodies: An array ofMatter.Bodyobjects to test against (e.g.,Matter.Composite.allBodies(engine.world)).point: An object containing{ x, y }coordinates.- Returns: An array containing all bodies that overlap the specified point. If no bodies overlap, it returns an empty array.
Step-by-Step Implementation
1. Listen for the Click Event
Attach an event listener to the canvas element running the Matter.js renderer.
const canvas = render.canvas;
canvas.addEventListener('click', (event) => {
// Coordinate conversion and query logic goes here
});2. Get Canvas-Relative Coordinates
Mouse events provide screen or viewport coordinates. You must adjust
these to match the canvas coordinate space using
getBoundingClientRect():
const rect = canvas.getBoundingClientRect();
const mousePosition = {
x: event.clientX - rect.left,
y: event.clientY - rect.top
};If your canvas uses CSS scaling (where the display size differs from
canvas.width and canvas.height), scale the
coordinates proportionally:
const mousePosition = {
x: (event.clientX - rect.left) * (canvas.width / rect.width),
y: (event.clientY - rect.top) * (canvas.height / rect.height)
};3. Run the Query
Fetch all active bodies from the engine's world, pass them to
Matter.Query.point, and evaluate the results:
const allBodies = Matter.Composite.allBodies(engine.world);
const clickedBodies = Matter.Query.point(allBodies, mousePosition);
if (clickedBodies.length > 0) {
// The top-most or first detected body
const targetBody = clickedBodies[0];
console.log('Body clicked:', targetBody);
// Example action: Remove the clicked body
Matter.Composite.remove(engine.world, targetBody);
}Complete Working Example
const { Engine, Render, Runner, Bodies, Composite, Query } = Matter;
// 1. Setup 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);
// 2. Add Bodies
const boxA = Bodies.rectangle(400, 200, 80, 80, { render: { fillStyle: '#e74c3c' } });
const boxB = Bodies.rectangle(450, 50, 80, 80, { render: { fillStyle: '#3498db' } });
const ground = Bodies.rectangle(400, 590, 810, 60, { isStatic: true });
Composite.add(engine.world, [boxA, boxB, ground]);
// 3. Click Detection via Matter.Query.point
render.canvas.addEventListener('click', (event) => {
const rect = render.canvas.getBoundingClientRect();
const clickPoint = {
x: (event.clientX - rect.left) * (render.canvas.width / rect.width),
y: (event.clientY - rect.top) * (render.canvas.height / rect.height)
};
const bodies = Composite.allBodies(engine.world);
const matches = Query.point(bodies, clickPoint);
if (matches.length > 0) {
const clickedBody = matches[0];
// Ignore static bodies like the ground
if (!clickedBody.isStatic) {
// Apply an upward impulse to the clicked body
Matter.Body.applyForce(clickedBody, clickedBody.position, {
x: 0,
y: -0.05
});
}
}
});Handling Overlapping Bodies
If multiple bodies overlap at the clicked point,
Matter.Query.point returns all of them in the array. If you
only want to affect the body that visually appears on top, sort or
filter the array using custom rendering layers or creation order before
taking action.