How to Simulate Maglev Trains in Matter.js
This guide explains how to simulate a stable magnetic levitation (maglev) train floating above track bodies using the Matter.js 2D physics engine. Because Matter.js does not provide built-in electromagnetic field mechanics, achieving stable levitation requires continuously measuring the distance between the train and the track and applying dynamic restorative forces. By utilizing a Proportional-Derivative (PD) controller within the engine’s update loop, you can eliminate perpetual bouncing, prevent clipping, and keep the train hovering smoothly.
1. The Physics Strategy: PD Control Loop
Simulating magnetic repulsion with a simple upward spring force causes the train to oscillate violently or fly off the track. To achieve equilibrium, use a Proportional-Derivative (PD) controller:
- Proportional Term (\(P\)): Pushes upward proportionally to how close the train is to the track relative to the target hover height.
- Derivative Term (\(D\)): Acts as a shock absorber (damping) by resisting the train's vertical velocity to cancel out bouncing.
The force applied per physics tick follows this formula: \[\text{Force}_y = -(k_p \times (\text{targetHeight} - \text{currentHeight}) - k_d \times \text{velocity}_y)\]
2. Setting Up Bodies and Collision Filters
Create the track as a static body and the train as a dynamic body. To avoid unwanted physics glitches when forces balance, disable friction on both objects. You can also use Matter.js collision categories to prevent physical mesh collisions between the train and the track, letting the custom repulsion force handle all separation.
const track = Matter.Bodies.rectangle(400, 500, 800, 40, {
isStatic: true,
friction: 0
});
const train = Matter.Bodies.rectangle(400, 300, 150, 40, {
friction: 0,
frictionAir: 0.001,
mass: 5
});
Matter.Composite.add(engine.world, [track, train]);3. Implementing the Levitation Loop
Attach a listener to the beforeUpdate event of your
Matter.js engine. In each frame, determine the clearance beneath
multiple points of the train (such as the front and rear) to maintain
pitch stability.
const TARGET_HOVER_DISTANCE = 40;
const kP = 0.005; // Repulsion strength
const kD = 0.08; // Damping strength
Matter.Events.on(engine, 'beforeUpdate', () => {
// Measure suspension points (left and right of center for pitch stability)
const suspensionOffsets = [-50, 50];
suspensionOffsets.forEach(offsetX => {
const rayOrigin = {
x: train.position.x + offsetX,
y: train.position.y + 20
};
// Raycast down toward the track
const rayEnd = { x: rayOrigin.x, y: rayOrigin.y + 100 };
const collisions = Matter.Query.ray([track], rayOrigin, rayEnd);
if (collisions.length > 0) {
const hit = collisions[0];
const currentDistance = hit.point.y - rayOrigin.y;
const distanceError = TARGET_HOVER_DISTANCE - currentDistance;
// Calculate vertical velocity at this corner
const verticalVelocity = train.velocity.y;
// Compute PD response
const upwardForceMagnitude = (distanceError * kP) - (verticalVelocity * kD);
// Apply force upwards when within repulsion range
if (currentDistance < TARGET_HOVER_DISTANCE * 1.5) {
Matter.Body.applyForce(
train,
{ x: rayOrigin.x, y: rayOrigin.y },
{ x: 0, y: -Math.max(0, upwardForceMagnitude) }
);
}
}
});
});4. Lateral Guidance and Propulsion
Hovering eliminates contact friction, meaning any horizontal force will slide the train indefinitely:
- Propulsion: Apply a small horizontal force vector
along the train's local X-axis using
Matter.Body.applyForce(train, train.position, { x: 0.002, y: 0 }). - Lateral Centering: If simulating a curved or uneven track, replicate horizontal magnetic guide-rails by measuring lateral displacement from the track center and applying a small corrective horizontal PD force to keep the carriage aligned.