How to Build a Telescopic Piston in Matter.js
This guide explains how to construct a rigid, linear telescopic piston joint in Matter.js by combining dual parallel constraints with custom length actuation. Because Matter.js lacks a native prismatic or slider joint, creating a stable linear actuator requires preventing rotational flex and tuning solver iterations. Below is a direct, step-by-step breakdown of how to configure the rigid bodies, constrain their motion to a single axis, and programmatically drive expansion and contraction.
1. The Core Architecture
A single Matter.js constraint functions like a distance joint, allowing connected bodies to rotate freely around their anchor points. To create a rigid telescopic joint that moves exclusively along one axis without bending, you must:
- Connect two bodies using two parallel constraints spaced across the width of the piston.
- Set the constraint stiffness to maximum (
1.0). - Increment or decrement the target
lengthof both constraints simultaneously to simulate pneumatic or hydraulic stroke. - Increase engine solver iterations to eliminate sponginess.
2. Setting Up the Bodies
Create a stationary base (or parent body) and the extending rod. Set collision filters so the piston rod does not snag on the base body if they overlap.
const { Engine, Render, Runner, Bodies, Composite, Constraint, Events } = Matter;
// Piston base (cylinder housing)
const base = Bodies.rectangle(400, 300, 60, 100, {
isStatic: true,
collisionFilter: { group: -1 }
});
// Extending rod
const rod = Bodies.rectangle(400, 200, 40, 100, {
collisionFilter: { group: -1 }
});3. Creating Dual Parallel Constraints
To prevent lateral wobble and rotation, place two identical constraints parallel to the axis of movement, offset along the X-axis.
const spacing = 15; // Offset from center
const initialLength = 100;
const constraintLeft = Constraint.create({
bodyA: base,
pointA: { x: -spacing, y: 0 },
bodyB: rod,
pointB: { x: -spacing, y: 0 },
stiffness: 1.0,
damping: 0.1,
length: initialLength
});
const constraintRight = Constraint.create({
bodyA: base,
pointA: { x: spacing, y: 0 },
bodyB: rod,
pointB: { x: spacing, y: 0 },
stiffness: 1.0,
damping: 0.1,
length: initialLength
});
Composite.add(world, [base, rod, constraintLeft, constraintRight]);4. Actuating Linear Expansion and Contraction
To extend and retract the piston, adjust the length
property of both constraints synchronously inside the
beforeUpdate event loop.
let targetLength = initialLength;
let extending = true;
const minLength = 60;
const maxLength = 220;
const speed = 2;
Events.on(engine, 'beforeUpdate', () => {
if (extending) {
targetLength += speed;
if (targetLength >= maxLength) extending = false;
} else {
targetLength -= speed;
if (targetLength <= minLength) extending = true;
}
constraintLeft.length = targetLength;
constraintRight.length = targetLength;
});5. Tuning for Absolute Stiffness
By default, Matter.js allows slight constraint stretching under heavy loads. To make the piston completely rigid:
- Increase Constraint Iterations: Raise
engine.constraintIterationsfrom the default of2to10or higher. - Increase Position Iterations: Raise
engine.positionIterationsto8or10to avoid penetration when the piston exerts force on other objects. - Normalize Mass: Ensure the mass of the rod is not drastically lower than the masses of the objects it pushes, or the solver will produce visible drift along the linear path.