Matter.js Variable Spring Plunger Mechanism
This article explains how to build a pinball-style plunger launch mechanism with variable spring compression in Matter.js. You will learn how to configure the physics bodies, attach an elastic constraint to act as a spring, control pull-back distance through user input, and release accumulated potential energy to launch a ball with varying force.
1. Conceptual Overview
A functional variable plunger consists of three primary components:
- Plunger Head/Rod: A dynamic body restricted to move along a single linear axis (typically the Y-axis).
- Spring Constraint: An elastic
Constraintconnecting the plunger to a fixed anchor point. - Compression Control: Logic that pulls the plunger away from its resting state based on user input, increasing the spring displacement before releasing it to transfer momentum to a dynamic projectile.
2. Setting Up the Bodies and Constraint
Define the plunger head as a dynamic rectangle and anchor it using an elastic constraint. An additional static stopper body prevents the plunger from flying past its resting position into the playfield.
const { Engine, Render, Runner, Bodies, Composite, Constraint, Body } = Matter;
// Create engine and world
const engine = Engine.create();
const world = engine.world;
// Resting coordinates
const restX = 300;
const restY = 500;
// Plunger body
const plunger = Bodies.rectangle(restX, restY, 40, 20, {
density: 0.05,
frictionAir: 0.001,
restitution: 0
});
// Fixed anchor behind the plunger
const anchor = { x: restX, y: restY + 100 };
// Spring constraint
const spring = Constraint.create({
pointA: anchor,
bodyB: plunger,
pointB: { x: 0, y: 0 },
stiffness: 0.08,
damping: 0.02,
render: {
visible: true,
lineWidth: 4,
strokeStyle: '#ff5722'
}
});
// Mechanical stops to limit movement
const topStopper = Bodies.rectangle(restX, restY - 5, 60, 5, {
isStatic: true,
isSensor: true // Set to false if you want hard collision instead of scripted limitation
});
Composite.add(world, [plunger, spring, topStopper]);3. Implementing Variable Compression Logic
To achieve variable compression, track the duration of a key press (such as the spacebar or down arrow). As the key is held, apply a continuous displacement downward until a maximum compression distance is reached.
let isCompressing = false;
const maxCompression = 80; // Maximum pixels the spring can be pulled back
const pullSpeed = 2; // Distance pulled per frame
window.addEventListener('keydown', (event) => {
if (event.code === 'Space') {
isCompressing = true;
}
});
window.addEventListener('keyup', (event) => {
if (event.code === 'Space') {
isCompressing = false;
}
});4. Updating and Releasing the Plunger
Use Matter.js's beforeUpdate event to handle the
pull-back and axis-locking logic. While the compression key is active,
smoothly shift the plunger down. When released, the physics engine takes
over, contracting the spring and firing the plunger upward.
Matter.Events.on(engine, 'beforeUpdate', () => {
// Keep plunger locked strictly to the vertical axis
Body.setPosition(plunger, { x: restX, y: plunger.position.y });
Body.setVelocity(plunger, { x: 0, y: plunger.velocity.y });
Body.setAngle(plunger, 0);
Body.setAngularVelocity(plunger, 0);
if (isCompressing) {
// Compress the spring by moving the plunger backward
if (plunger.position.y < restY + maxCompression) {
Body.setPosition(plunger, {
x: restX,
y: plunger.position.y + pullSpeed
});
Body.setVelocity(plunger, { x: 0, y: 0 });
}
} else {
// Prevent the spring from launching the plunger beyond its resting position
if (plunger.position.y < restY) {
Body.setPosition(plunger, { x: restX, y: restY });
Body.setVelocity(plunger, { x: 0, y: 0 });
}
}
});5. Transferring Momentum to the Ball
Place a dynamic ball resting directly on top of the plunger head:
const ball = Bodies.circle(restX, restY - 20, 15, {
density: 0.004,
restitution: 0.2
});
Composite.add(world, ball);When the plunger is pulled back and released, the stored tension in
the constraint snaps the plunger upward to restY,
transferring kinetic energy directly to the ball upon collision before
the plunger stops at its rest boundary. Longer hold times yield greater
displacement, delivering higher launch velocities.