How to Create Pinball Flippers in Matter.js
This guide explains how to construct a fast, responsive pinball flipper in Matter.js that snaps briskly to an exact stop angle. You will learn how to create the flipper body, anchor it to a pivot point using a constraint, and manipulate angular velocity and angle clamping inside the engine's update loop to ensure instantaneous reaction times and rigid angle limits.
1. Create the Flipper Body and Pivot Constraint
To create a realistic flipper, define an elongated polygon or rounded rectangle and secure one of its ends to a fixed point in space using a revolute constraint.
const { Bodies, Body, Constraint, World } = Matter;
// Pivot coordinates
const pivotX = 200;
const pivotY = 500;
const flipperLength = 100;
const flipperWidth = 20;
// Position the body center offset from the pivot point
const flipper = Bodies.rectangle(
pivotX + flipperLength / 2,
pivotY,
flipperLength,
flipperWidth,
{
chamfer: { radius: 10 },
density: 0.005,
restitution: 0.1, // Low restitution prevents unwanted jitter on hard stops
friction: 0.0
}
);
// Pin the flipper at the pivot end
const pivotConstraint = Constraint.create({
pointA: { x: pivotX, y: pivotY },
bodyB: flipper,
pointB: { x: -flipperLength / 2, y: 0 },
stiffness: 1,
length: 0
});
World.add(engine.world, [flipper, pivotConstraint]);2. Define Stroke Limits and Control State
Define the resting angle, the active (flipped) angle, and a boolean state flag to handle user input. For a left flipper, angles are typically negative when flipped up and slightly positive or flat at rest.
const DEG_TO_RAD = Math.PI / 180;
const flipperConfig = {
minAngle: 25 * DEG_TO_RAD, // Resting angle
maxAngle: -35 * DEG_TO_RAD, // Flipped up angle
speed: 0.35, // Angular speed (radians per frame)
isFlipping: false
};
// Listen for keyboard controls
window.addEventListener('keydown', (e) => {
if (e.code === 'KeyZ') flipperConfig.isFlipping = true;
});
window.addEventListener('keyup', (e) => {
if (e.code === 'KeyZ') flipperConfig.isFlipping = false;
});3. Implement Brisk Rotation and Precise Clamping
Physics-based torque often leads to spongy deceleration or bouncy
overshoot. The most reliable method to achieve an instant, crisp flip is
setting the flipper's angular velocity directly during the
beforeUpdate engine event, and clamping the angle
immediately once it crosses the threshold.
const { Events } = Matter;
Events.on(engine, 'beforeUpdate', () => {
const currentAngle = flipper.angle;
const targetAngle = flipperConfig.isFlipping ? flipperConfig.maxAngle : flipperConfig.minAngle;
const direction = targetAngle < currentAngle ? -1 : 1;
// Check if the flipper has reached or passed the target angle
const hasReachedTarget = direction === -1
? currentAngle <= targetAngle
: currentAngle >= targetAngle;
if (hasReachedTarget) {
// Snap to the exact angle and nullify velocity
Body.setAngle(flipper, targetAngle);
Body.setAngularVelocity(flipper, 0);
} else {
// Drive the flipper briskly toward the target angle
Body.setAngularVelocity(flipper, direction * flipperConfig.speed);
}
});4. Prevent Ball Tunneling During High-Speed Impact
Because the flipper moves across a wide arc in only a few frames, small or high-speed balls may pass directly through the flipper (tunneling). Prevent this by tuning the physics engine runner:
- Increase Engine Iterations: Raise
positionIterationsandvelocityIterationson the engine options to a minimum of8or10. - Substep the Physics Runner: If using
Matter.Runner, lower the time step or execute multipleEngine.update(engine, delta)cycles per animation frame so the collision solver detects contacts throughout the entire stroke.