Implement Wall Jump with Side Sensors in Matter.js
Implementing a responsive wall jump mechanic in Matter.js requires detecting surface contact along the player's sides without generating unwanted physical friction. This article covers how to build a compound body consisting of a main player collider and two lateral sensor bodies, listen for collision events using the Matter.js event system, and apply directional jump impulses based on the active wall contact state.
1. Constructing the Compound Player Body
To detect walls reliably, attach separate sensor bodies to the left and right sides of the main player body. Sensors detect collisions via events without exerting physical reaction forces on the objects they touch.
const { Bodies, Body } = Matter;
const playerWidth = 40;
const playerHeight = 60;
const sensorThickness = 6;
// Main collision body
const mainBody = Bodies.rectangle(0, 0, playerWidth, playerHeight, {
label: 'playerMain',
friction: 0.001
});
// Left and right side sensors
const leftSensor = Bodies.rectangle(
-playerWidth / 2 - sensorThickness / 2,
0,
sensorThickness,
playerHeight * 0.8,
{
isSensor: true,
label: 'leftSensor'
}
);
const rightSensor = Bodies.rectangle(
playerWidth / 2 + sensorThickness / 2,
0,
sensorThickness,
playerHeight * 0.8,
{
isSensor: true,
label: 'rightSensor'
}
);
// Combine parts into a single compound body
const player = Body.create({
parts: [mainBody, leftSensor, rightSensor],
inertia: Infinity // Prevent the player from rotating
});2. Tracking Wall Contact States
Track when sensors enter and exit collisions with environment bodies (such as walls or platforms) using Matter's collision events.
const wallContact = {
left: 0,
right: 0
};
Matter.Events.on(engine, 'collisionStart', (event) => {
event.pairs.forEach((pair) => {
const { bodyA, bodyB } = pair;
// Check left sensor
if (bodyA.label === 'leftSensor' || bodyB.label === 'leftSensor') {
const other = bodyA.label === 'leftSensor' ? bodyB : bodyA;
if (other.label === 'wall') wallContact.left++;
}
// Check right sensor
if (bodyA.label === 'rightSensor' || bodyB.label === 'rightSensor') {
const other = bodyA.label === 'rightSensor' ? bodyB : bodyA;
if (other.label === 'wall') wallContact.right++;
}
});
});
Matter.Events.on(engine, 'collisionEnd', (event) => {
event.pairs.forEach((pair) => {
const { bodyA, bodyB } = pair;
if (bodyA.label === 'leftSensor' || bodyB.label === 'leftSensor') {
const other = bodyA.label === 'leftSensor' ? bodyB : bodyA;
if (other.label === 'wall') wallContact.left = Math.max(0, wallContact.left - 1);
}
if (bodyA.label === 'rightSensor' || bodyB.label === 'rightSensor') {
const other = bodyA.label === 'rightSensor' ? bodyB : bodyA;
if (other.label === 'wall') wallContact.right = Math.max(0, wallContact.right - 1);
}
});
});Using counter increments instead of booleans prevents state bugs when a sensor overlaps multiple wall tiles simultaneously.
3. Applying the Wall Jump Force
When the jump input is triggered, inspect wallContact.
If touching a wall, launch the player away from the wall horizontally
and upward vertically. Setting velocity directly ensures snappy,
consistent movement.
const JUMP_UP_FORCE = -12;
const JUMP_AWAY_FORCE = 8;
function handleJump() {
const isTouchingLeft = wallContact.left > 0;
const isTouchingRight = wallContact.right > 0;
if (isTouchingLeft) {
// Jump away to the right and upward
Matter.Body.setVelocity(player, {
x: JUMP_AWAY_FORCE,
y: JUMP_UP_FORCE
});
} else if (isTouchingRight) {
// Jump away to the left and upward
Matter.Body.setVelocity(player, {
x: -JUMP_AWAY_FORCE,
y: JUMP_UP_FORCE
});
}
}This implementation isolates contact detection to designated side regions, prevents wall sticking, and yields precise control over wall-jumping trajectories.