How to Simulate Magnetic Repulsion in Matter.js
This article explains how to simulate magnetic repulsion between specific rigid bodies using the Matter.js 2D physics engine. By listening to the engine's update loop, calculating distance vectors between targeted bodies, and applying opposing forces using Coulomb’s Law or inverse-square distance models, you can create realistic repulsive magnetic behaviors.
Core Concept
Matter.js does not contain a native magnetism plugin, but it provides
the Matter.Body.applyForce() method and the
beforeUpdate engine event. Magnetic repulsion is achieved
by:
- Identifying pairs of bodies that should repel each other.
- Calculating the Euclidean distance and directional vector between them.
- Calculating force magnitude inversely proportional to the distance (or distance squared).
- Applying equal and opposite forces to push the bodies apart before each physics step.
Implementation Steps
1. Tag Your Magnetic Bodies
Add a custom property or label to identify which bodies should participate in magnetic repulsion.
const bodyA = Matter.Bodies.circle(200, 300, 20, {
label: 'magnetic',
magneticCharge: 1
});
const bodyB = Matter.Bodies.circle(260, 300, 20, {
label: 'magnetic',
magneticCharge: 1
});
Matter.Composite.add(engine.world, [bodyA, bodyB]);2. Hook Into the
beforeUpdate Event
Attach an event listener to
Matter.Events.on(engine, 'beforeUpdate', ...) to compute
and apply forces continuously before the engine updates positions.
Matter.Events.on(engine, 'beforeUpdate', () => {
applyMagneticRepulsion(bodyA, bodyB, 5000);
});3. Calculate and Apply the Repulsive Force
To avoid extreme physics glitches when bodies overlap (which causes division by zero or massive acceleration), define a minimum interaction distance threshold.
function applyMagneticRepulsion(bodyA, bodyB, strength = 1000) {
// 1. Calculate delta vector
const dx = bodyB.position.x - bodyA.position.x;
const dy = bodyB.position.y - bodyA.position.y;
// 2. Calculate distance
const distanceSquared = dx * dx + dy * dy;
const distance = Math.sqrt(distanceSquared);
// Minimum distance threshold to prevent infinite force
const minDistance = 30;
// Maximum range of the magnetic field
const maxDistance = 300;
if (distance < minDistance || distance > maxDistance) {
return;
}
// 3. Normalized directional vector
const normalX = dx / distance;
const normalY = dy / distance;
// 4. Inverse-square law: Force = strength / distance^2
const forceMagnitude = strength / distanceSquared;
const force = {
x: normalX * forceMagnitude,
y: normalY * forceMagnitude
};
// 5. Apply opposing forces
// Push bodyA away from bodyB
Matter.Body.applyForce(bodyA, bodyA.position, {
x: -force.x,
y: -force.y
});
// Push bodyB away from bodyA
Matter.Body.applyForce(bodyB, bodyB.position, {
x: force.x,
y: force.y
});
}Managing Multiple Magnetic Bodies
When managing multiple bodies, iterate through all pairs without duplicating calculations.
const magneticBodies = [/* array of your magnetic bodies */];
Matter.Events.on(engine, 'beforeUpdate', () => {
for (let i = 0; i < magneticBodies.length; i++) {
for (let j = i + 1; j < magneticBodies.length; j++) {
applyMagneticRepulsion(magneticBodies[i], magneticBodies[j], 3000);
}
}
});Stability Tips
- Distance Clamping: Always clamp the minimum distance to roughly the sum of the bodies' collision radii so forces stay stable during collisions.
- Max Distance Culling: Always use a maximum distance
threshold (
maxDistance) to avoid performing unnecessary force calculations on distant bodies. - Linear Falloff Alternative: For softer, less
volatile repulsion, substitute inverse-square falloff
(
strength / distanceSquared) with linear falloff (strength * (1 - distance / maxDistance)).