Make Bodies Float Upward in Matter.js

Simulating a helium-filled balloon in Matter.js requires counteracting the default downward engine gravity and introducing an upward buoyant force. Because Matter.js applies gravity globally across the entire physics world, individual bodies cannot have negative gravity assigned directly to their properties. This guide demonstrates how to achieve a realistic helium effect by applying a continuous upward force on a specific body using the engine's update loop, combined with air resistance adjustments for a natural terminal ascent velocity.


Understanding the Physics in Matter.js

By default, a Matter.js world applies downward acceleration along the Y-axis defined by engine.gravity.y and engine.gravity.scale. The downward force exerted on any rigid body during a step is:

\[\text{Force}_{\text{gravity}} = \text{body.mass} \times \text{engine.gravity.y} \times \text{engine.gravity.scale}\]

To make a body rise, you must continuously apply an upward force along the negative Y-axis that exceeds this gravitational pull.


Step-by-Step Implementation

1. Create the Helium Body with Air Resistance

When an object floats, air resistance prevents it from accelerating upward indefinitely. Set a moderate frictionAir value on the body so it quickly reaches a stable, realistic terminal velocity.

const { Bodies } = Matter;

const balloon = Bodies.circle(400, 500, 30, {
    density: 0.001,
    frictionAir: 0.05, // Prevents infinite upward acceleration
    restitution: 0.8
});

Matter.Composite.add(engine.world, balloon);

2. Apply Upward Force in the Update Loop

Listen to the beforeUpdate event on the engine to apply an upward force before physics calculations are executed on each frame.

const { Events, Body } = Matter;

Events.on(engine, 'beforeUpdate', () => {
    // Calculate the exact force needed to counteract gravity
    const gravityForce = balloon.mass * engine.gravity.y * engine.gravity.scale;

    // Define additional buoyant force to push it upward
    const buoyancy = 0.0015;

    // Apply the total upward force (negative Y direction)
    Body.applyForce(balloon, balloon.position, {
        x: 0,
        y: -(gravityForce + buoyancy)
    });
});

Adding Realism: Simulating Wind and Oscillation

Helium balloons rarely rise in a straight vertical line. You can introduce a slight horizontal wobble using a sine wave inside the same update loop:

Events.on(engine, 'beforeUpdate', (event) => {
    const gravityForce = balloon.mass * engine.gravity.y * engine.gravity.scale;
    const buoyancy = 0.0015;
    
    // Create a gentle side-to-side drift
    const sway = Math.sin(event.timestamp * 0.003) * 0.0002;

    Body.applyForce(balloon, balloon.position, {
        x: sway,
        y: -(gravityForce + buoyancy)
    });
});

Alternative: Inverting Gravity Globally

If every body in your simulation represents a floating object, you do not need per-body force loops. Instead, set the global gravity to a negative value:

engine.gravity.y = -1;

For mixed environments containing both floating balloons and standard falling objects, the continuous Body.applyForce method within beforeUpdate remains the standard and most reliable solution.