Invert Gravity for Specific Bodies in Matter.js
Matter.js simulates a uniform global gravity vector across all active
bodies in an engine, offering no built-in, per-body gravity toggles.
However, you can easily invert gravity for individual bodies without
altering the global engine.gravity configuration by hooking
into the simulation's update loop and applying an opposing corrective
force on every frame. This technique cancels standard downward gravity
and imparts an equivalent upward acceleration exclusively to targeted
objects.
The Mathematical Principle
Matter.js calculates gravitational force on a body as:
\[\vec{F}_{\text{gravity}} = m \cdot \vec{g} \cdot s\]
Where:
- \(m\) is the body's mass
(
body.mass). - \(\vec{g}\) represents the
directional gravity vector (
engine.gravity.x,engine.gravity.y). - \(s\) is the gravity scaling factor
(
engine.gravity.scale, which defaults to0.001).
Because the physics engine automatically applies this force downward each step, merely applying an equal upward force would result in zero net acceleration (weightlessness). To achieve inverted gravity of equal magnitude in the opposite direction, you must apply twice the gravitational force in the inverted direction: one unit to cancel the world gravity, and one unit to accelerate the body upward.
Implementation via
beforeUpdate
The standard approach uses the
Matter.Events.on(engine, 'beforeUpdate', callback) event
hook. Within this callback, iterate over your custom collection of
inverted bodies and use Matter.Body.applyForce() to apply
the counter-force.
const { Engine, Render, Runner, Bodies, Composite, Events, Body } = Matter;
// Initialize engine and world
const engine = Engine.create();
const world = engine.world;
// Create a standard body and an anti-gravity body
const standardBox = Bodies.rectangle(300, 100, 50, 50);
const invertedBox = Bodies.rectangle(500, 500, 50, 50, {
render: { fillStyle: '#ff4d4d' }
});
// Custom flag or property to identify inverted bodies
invertedBox.customGravityMultiplier = -1;
Composite.add(world, [standardBox, invertedBox]);
// Apply opposing forces prior to every engine update
Events.on(engine, 'beforeUpdate', () => {
const bodies = Composite.allBodies(world);
const gravity = engine.gravity;
bodies.forEach((body) => {
if (body.customGravityMultiplier !== undefined && !body.isStatic) {
// Calculate normal gravity force applied by the engine
const baseForceX = body.mass * gravity.x * gravity.scale;
const baseForceY = body.mass * gravity.y * gravity.scale;
// When customGravityMultiplier is -1:
// Required upward force = -2 * baseForce
const correctiveFactor = body.customGravityMultiplier - 1;
const force = {
x: baseForceX * correctiveFactor,
y: baseForceY * correctiveFactor
};
Body.applyForce(body, body.position, force);
}
});
});Considerations for Accuracy
- Sleeping Bodies: If
engine.enableSleepingis enabled, sleeping bodies will not trigger physics updates properly until awakened. Wake the body withMatter.Sleeping.set(body, false)if its gravity direction changes dynamically. - Non-linear Multipliers: By structuring the
corrective factor as
(customGravityMultiplier - 1) * baseForce, you can simulate any custom gravitational scale per body:0results in zero gravity (cancels normal gravity completely).-1produces exactly inverted gravity.2produces double regular gravity.
- Air Resistance: Upward-accelerating bodies are
still governed by
body.frictionAir. If an object is not floating as rapidly as anticipated, reduce its air friction (default is0.01).