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:

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:

4. Tuning the Feel