Floating Buoyancy Platforms in Matter.js
This article explains how to simulate interactive, buoyant platforms in the Matter.js 2D physics engine that submerge under weight and naturally bob back to the surface. Because Matter.js does not include a native fluid dynamics system, this effect is achieved by calculating water displacement manually and applying upward restoring forces combined with linear drag during the engine's update cycle.
1. The Physics Concept
To simulate a floating object that dips when landed on, you need two primary forces applied on every physics tick:
- Buoyant Force (Spring Force): An upward force proportional to how deep the platform is submerged beneath the target water line.
- Fluid Resistance (Damping Force): A drag force acting opposite to the platform's vertical velocity to absorb kinetic energy and prevent infinite oscillation.
Together, these simulate a damped harmonic oscillator, allowing the platform to sink when extra mass (like a player character) lands on it, and smoothly float back up once the mass leaves.
2. Implementation Code
You can attach an update hook using
Matter.Events.on(engine, 'beforeUpdate', callback) to
compute and apply these forces directly to the platform body.
const { Engine, Render, Runner, Bodies, Composite, Body, Events, Vector } = Matter;
// 1. Setup Engine & World
const engine = Engine.create();
const world = engine.world;
// 2. Define Water Level and Platform Parameters
const waterLevel = 350; // The Y-coordinate of the water surface
const buoyancyStiffness = 0.004; // Strength of the upward buoyant push
const waterDamping = 0.05; // Drag factor to stop bouncing
// 3. Create the Buoyant Platform
const platform = Bodies.rectangle(400, waterLevel, 200, 40, {
density: 0.001,
frictionAir: 0.01 // Standard air resistance
});
// Constrain horizontal motion so the platform only moves vertically
const verticalConstraint = Matter.Constraint.create({
pointA: { x: platform.position.x, y: waterLevel },
bodyB: platform,
pointB: { x: 0, y: 0 },
stiffness: 0.1,
length: 0
});
Composite.add(world, [platform]);
// 4. Apply Buoyancy and Drag Forces
Events.on(engine, 'beforeUpdate', () => {
const depth = platform.position.y - waterLevel;
// Check if the platform is below the water surface
if (depth > 0) {
// Upward force proportional to displacement depth
const buoyantForce = depth * buoyancyStiffness;
// Opposing damping force based on current vertical velocity
const dampingForce = platform.velocity.y * waterDamping;
// Net vertical force (negative Y is upward in Matter.js)
const netForceY = -(buoyantForce - dampingForce);
// Apply force at the center of mass
Body.applyForce(platform, platform.position, {
x: 0,
y: netForceY
});
}
});3. Preventing Unwanted Rotation and Drift
By default, an unconstrained rigid body can flip over or drift sideways when jumped on off-center:
- Locking Rotation: Set
Body.setInertia(platform, Infinity)to prevent the platform from tilting entirely, keeping it strictly horizontal. - Limiting Horizontal Movement: Use a soft constraint
(
Matter.Constraint) anchored to the platform's horizontal anchor coordinate, or clampplatform.position.xinside thebeforeUpdateevent to prevent drifting without interfering with vertical displacement.
4. Tuning the Feel
- Deeper Dip: Lower the
buoyancyStiffnessvalue or increase the player's mass. - Snappier Recovery: Increase the
buoyancyStiffnessvalue. - Water Viscosity: Increase
waterDampingto simulate thicker liquids like mud or oil, or decrease it for a lighter, bouncier water effect.