Build an Arcade Claw Machine with Matter.js

This guide explains how to construct a functional arcade claw machine simulation using the Matter.js 2D physics engine. You will learn how to configure the physics world, assemble a multi-part mechanical claw using rigid bodies and constraints, implement responsive closing mechanics, and calibrate physics parameters to allow the prongs to reliably grab and lift prize objects.

Initializing the World and Machine Boundaries

Start by setting up the standard Matter.js modules: Engine, Render, Runner, Bodies, Composite, and Constraint. Define the physical play area by creating static rectangular bodies for the floor and side walls, leaving the top open or wide enough for the claw trolley to traverse.

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

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);

// Boundaries
const ground = Bodies.rectangle(400, 590, 800, 20, { isStatic: true });
const leftWall = Bodies.rectangle(10, 300, 20, 600, { isStatic: true });
const rightWall = Bodies.rectangle(790, 300, 20, 600, { isStatic: true });
Composite.add(world, [ground, leftWall, rightWall]);

Populating the Prize Pit

Spawn a cluster of dynamic bodies at the bottom of the machine to serve as prizes. A mixture of circles and rounded rectangles with varying masses and high friction values provides an authentic arcade experience.

for (let i = 0; i < 25; i++) {
    const x = 300 + Math.random() * 200;
    const y = 500 + Math.random() * 50;
    const prize = Bodies.circle(x, y, 20, {
        friction: 0.8,
        restitution: 0.2,
        density: 0.002
    });
    Composite.add(world, prize);
}

Building the Mechanical Claw Assembly

A functional claw requires a central carriage (base) and two or more articulated prongs. Each prong consists of an upper arm and an angled lower tip to hook beneath objects.

  1. Carriage Base: Create a kinematic or light dynamic body to act as the central hub.
  2. Prongs: Construct the left and right prongs. Combine parts using Body.create({ parts: [...] }) to create curved or angled shapes, or use single angled rectangles.
  3. Pivots: Connect each prong to the base using revolute constraints (Constraint.create with zero length).
const clawBase = Bodies.rectangle(400, 100, 60, 20, { isStatic: true });

// Left Prong
const leftProng = Bodies.rectangle(370, 150, 12, 80, { 
    friction: 0.9,
    angle: 0.2
});

// Right Prong
const rightProng = Bodies.rectangle(430, 150, 12, 80, { 
    friction: 0.9,
    angle: -0.2
});

// Pivot Constraints
const leftPivot = Constraint.create({
    bodyA: clawBase,
    pointA: { x: -25, y: 10 },
    bodyB: leftProng,
    pointB: { x: 0, y: -35 },
    stiffness: 1,
    length: 0
});

const rightPivot = Constraint.create({
    bodyA: clawBase,
    pointA: { x: 25, y: 10 },
    bodyB: rightProng,
    pointB: { x: 0, y: -35 },
    stiffness: 1,
    length: 0
});

Composite.add(world, [clawBase, leftProng, rightProng, leftPivot, rightPivot]);

Implementing the Closing and Opening Mechanism

To open and close the claw, use an elastic closing constraint connecting the bottom tips of the prongs, or apply rotational torque directly to the prongs.

An elastic spring constraint creates a natural gripping action that yields against solid prizes without destabilizing the physics simulation:

// Actuator constraint between the lower portions of the arms
const closingActuator = Constraint.create({
    bodyA: leftProng,
    pointA: { x: 0, y: 20 },
    bodyB: rightProng,
    pointB: { x: 0, y: 20 },
    stiffness: 0.05,
    length: 80, // Default open length
    render: { visible: false }
});

Composite.add(world, closingActuator);

function closeClaw() {
    // Shrink target distance to pull prongs inward
    closingActuator.length = 15;
    closingActuator.stiffness = 0.08;
}

function openClaw() {
    // Expand target distance to push prongs outward
    closingActuator.length = 80;
    closingActuator.stiffness = 0.05;
}

Alternatively, apply direct angular torque inside the game loop using Body.setAngularVelocity or leftProng.torque until the arms hit mechanical stops or grab a prize.

Controlling Machine States and Carriage Motion

Manage the claw through a state machine: IDLE, DROPPING, GRABBING, RETRACTING, and RELEASING.

Move the clawBase directly by altering its position using Body.setPosition:

let clawState = 'IDLE';

function updateClawMovement() {
    if (clawState === 'DROPPING') {
        Body.setPosition(clawBase, { x: clawBase.position.x, y: clawBase.position.y + 3 });
        if (clawBase.position.y >= 420) {
            clawState = 'GRABBING';
            closeClaw();
            setTimeout(() => { clawState = 'RETRACTING'; }, 1000);
        }
    } else if (clawState === 'RETRACTING') {
        Body.setPosition(clawBase, { x: clawBase.position.x, y: clawBase.position.y - 3 });
        if (clawBase.position.y <= 100) {
            clawState = 'IDLE';
            // Optional: Move to chute and call openClaw()
        }
    }
}

Matter.Events.on(engine, 'beforeUpdate', updateClawMovement);

Optimizing Friction and Grip Stability

Physics engines often suffer from slippery surfaces when lifting dynamic objects. To ensure successful grabs: