Create Sticky Quicksand Zones in Matter.js
Creating sticky quicksand zones in Matter.js requires combining a non-colliding sensor body with custom velocity damping applied on every engine update. This article covers how to define a designated trigger area using sensor properties, track bodies entering and leaving the zone, and rapidly decelerate both linear and angular velocities to simulate thick, viscous fluid resistance.
1. Define the Quicksand Area as a Sensor
Matter.js bodies normally bounce or block other rigid bodies. To
allow objects to enter the quicksand zone while still detecting their
presence, set the body's isSensor property to
true and mark it as isStatic.
const quicksandZone = Matter.Bodies.rectangle(400, 300, 250, 150, {
isStatic: true,
isSensor: true,
render: {
fillStyle: '#c2b280',
opacity: 0.6
}
});
Matter.Composite.add(engine.world, quicksandZone);2. Track Entities Inside the Zone
Maintain a set or array of dynamic bodies currently inside the quicksand volume by listening to the engine's collision events.
const submergedBodies = new Set();
Matter.Events.on(engine, 'collisionStart', (event) => {
event.pairs.forEach((pair) => {
if (pair.bodyA === quicksandZone && !pair.bodyB.isStatic) {
submergedBodies.add(pair.bodyB);
} else if (pair.bodyB === quicksandZone && !pair.bodyA.isStatic) {
submergedBodies.add(pair.bodyA);
}
});
});
Matter.Events.on(engine, 'collisionEnd', (event) => {
event.pairs.forEach((pair) => {
if (pair.bodyA === quicksandZone) {
submergedBodies.delete(pair.bodyB);
} else if (pair.bodyB === quicksandZone) {
submergedBodies.delete(pair.bodyA);
}
});
});3. Apply Linear and Angular Velocity Damping
To create a thick, sticky effect, modify the velocity components of
all trapped bodies directly inside the beforeUpdate event.
Multiplying velocities by a fraction below 1.0 reduces
speed rapidly without destabilizing the physics engine.
const LINEAR_DAMPING = 0.85; // Lower values mean thicker mud (range: 0.0 - 1.0)
const ANGULAR_DAMPING = 0.70; // Rapidly suppresses rotation
const SINK_GRAVITY = 0.2; // Optional: slow downward pull
Matter.Events.on(engine, 'beforeUpdate', () => {
submergedBodies.forEach((body) => {
// Damp linear velocity directly
Matter.Body.setVelocity(body, {
x: body.velocity.x * LINEAR_DAMPING,
y: (body.velocity.y * LINEAR_DAMPING) + SINK_GRAVITY
});
// Damp angular velocity directly
Matter.Body.setAngularVelocity(body, body.angularVelocity * ANGULAR_DAMPING);
});
});4. Alternative: Area Query Approach
If dynamic objects are spawned or destroyed while inside the
quicksand, collision listeners may occasionally miss cleanup events. You
can replace the event listener approach with
Matter.Query.region inside the update step for a more
robust implementation:
Matter.Events.on(engine, 'beforeUpdate', () => {
const overlappingBodies = Matter.Query.region(
Matter.Composite.allBodies(engine.world),
quicksandZone.bounds
);
overlappingBodies.forEach((body) => {
if (body === quicksandZone || body.isStatic) return;
Matter.Body.setVelocity(body, {
x: body.velocity.x * 0.85,
y: body.velocity.y * 0.85 + 0.15
});
Matter.Body.setAngularVelocity(body, body.angularVelocity * 0.75);
});
});Using this approach guarantees that any body currently overlapping
the quicksand boundary has its movement heavily suppressed immediately.
Adjust the damping constants toward 0.0 for concrete-like
entrapment or closer to 0.95 for light liquid drag.