Simulate Buoyancy in Water Using Matter.js
Simulating buoyancy in Matter.js requires applying custom upward forces and viscous drag to dynamic bodies whenever they enter a designated fluid region. Because Matter.js is a rigid-body physics engine without native fluid dynamics, developers must manually calculate immersion depth, buoyant force counteracting gravity, and fluid resistance to mimic natural floating behavior.
1. Define the Fluid Region
To simulate a body of water, create a static body with its
isSensor property set to true. This prevents
physical collisions while allowing you to detect when other objects
intersect the water.
const water = Matter.Bodies.rectangle(400, 500, 800, 200, {
isStatic: true,
isSensor: true,
render: { fillStyle: 'rgba(0, 150, 255, 0.4)' }
});
Matter.World.add(world, water);2. Track Immersion and Apply Forces
Hook into the Matter.js update loop using
Events.on(engine, 'beforeUpdate', callback). In each tick,
check which bodies overlap the fluid area using
Matter.Query.region or boundary checks, and apply the
appropriate forces.
Matter.Events.on(engine, 'beforeUpdate', () => {
const bodies = [box]; // Array of dynamic bodies to evaluate
const waterBounds = water.bounds;
bodies.forEach(body => {
// Check if the body intersects the water area
if (body.bounds.max.y > waterBounds.min.y && body.bounds.min.y < waterBounds.max.y) {
// Calculate submerged percentage based on vertical overlap
const submergedDepth = Math.min(body.bounds.max.y - waterBounds.min.y, body.bounds.max.y - body.bounds.min.y);
const bodyHeight = body.bounds.max.y - body.bounds.min.y;
const submergedRatio = Math.min(Math.max(submergedDepth / bodyHeight, 0), 1);
// 1. Buoyant Force (Archimedes' Principle)
// Opposes gravity: mass * gravity * submerged proportion * buoyancy scalar
const gravity = engine.gravity.scale * engine.gravity.y;
const buoyancyMagnitude = body.mass * gravity * submergedRatio * 1.5;
// 2. Drag Force (Fluid Resistance)
// Slows down the body linearly to simulate water viscosity
const dragFactor = 0.05 * submergedRatio;
const dragX = -body.velocity.x * dragFactor;
const dragY = -body.velocity.y * dragFactor;
// Apply combined force to body center
Matter.Body.applyForce(body, body.position, {
x: dragX,
y: -buoyancyMagnitude + dragY
});
// 3. Angular Damping
// Dampen rotational movement while submerged
Matter.Body.setAngularVelocity(body, body.angularVelocity * (1 - 0.05 * submergedRatio));
}
});
});3. Fine-Tune Fluid Parameters
- Density and Lift: Adjust the buoyancy scalar (set
to
1.5above). Values greater than1.0will make objects less dense than the fluid, causing them to float to the surface. Values below1.0will cause them to sink slowly. - Viscosity: Increase the
dragFactorto simulate heavier liquids such as oil or syrup, or decrease it for lighter fluids. - Partial Immersion: Using
submergedRatioensures that the body does not experience a sudden, jarring force upon touching the water's surface, resulting in smooth bobbing and stabilization.