Create Swinging Pendulum Blades in Matter.js

This guide demonstrates how to build functional swinging pendulum blades in Matter.js to serve as corridor traps in 2D games. You will learn how to configure composite bodies, attach them to static pivots using constraints, sustain realistic swinging motion, and handle collision detection for hazardous game environments.

1. Setting Up the Pivot and Blade

A pendulum trap consists of two main physics elements: a fixed anchor point and a swinging body that acts as the arm and blade.

While you can link separate bodies for the arm and the blade, creating a single compound body or a dense rectangular body simplifies physics calculations and eliminates unwanted joint flexing.

const { Engine, Render, Runner, Bodies, Composite, Constraint, Body, Vector } = Matter;

// Initialize engine and renderer
const engine = Engine.create();
const world = engine.world;
const render = Render.create({
    element: document.body,
    engine: engine,
    options: { width: 800, height: 600, wireframes: false }
});

Render.run(render);
Runner.run(Runner.create(), engine);

2. Creating the Pendulum Assembly

Define the blade body, position it below a designated pivot coordinate, and connect it using Matter.Constraint.

const pivotX = 400;
const pivotY = 100;
const armLength = 250;

// Create the blade (using a trapezoid or rectangle for the lethal edge)
const blade = Bodies.trapezoid(pivotX + armLength, pivotY, 60, 100, 0.5, {
    density: 0.05,
    frictionAir: 0.0005, // Low air resistance to sustain swing
    restitution: 0.8,
    render: { fillStyle: '#e74c3c' }
});

// Create the constraint connecting the blade to the fixed world point
const pendulumConstraint = Constraint.create({
    pointA: { x: pivotX, y: pivotY },
    bodyB: blade,
    pointB: { x: 0, y: -armLength / 2 },
    stiffness: 1,
    length: armLength,
    render: {
        strokeStyle: '#bdc3c7',
        lineWidth: 4
    }
});

Composite.add(world, [blade, pendulumConstraint]);

3. Sustaining the Pendulum Swing

Natural friction and gravity will eventually bring a standard pendulum to a stop. To create an infinite hazard that sweeps across a corridor, you can periodically apply torque or manually adjust the body's velocity using the beforeUpdate engine event.

Matter.Events.on(engine, 'beforeUpdate', () => {
    // Keep the pendulum swinging by checking its angle relative to the pivot
    const currentAngle = blade.angle;
    
    // Apply a subtle continuous driving force if velocity drops
    if (Math.abs(blade.angularVelocity) < 0.02) {
        const direction = Math.sin(currentAngle) > 0 ? -1 : 1;
        Body.applyForce(blade, blade.position, {
            x: direction * 0.05,
            y: 0
        });
    }
});

Alternatively, you can set blade.frictionAir = 0 to preserve kinetic energy indefinitely, provided the simulation receives an initial offset displacement when created.

4. Configuring Corridor Collision

Pendulum blades must interact with players while avoiding unwanted collisions with the ceiling or corridor walls: