How to Build a Matter.js Seesaw with Ground Stoppers

This guide explains how to construct a functional seesaw (teeter-totter) physics simulation using Matter.js. You will learn how to create the balance plank, bind it to a fixed central pivot point using constraints, and place static ground stoppers to restrict the plank's maximum rotation angle for realistic behavior.

1. Setting Up the Matter.js Environment

Before creating the seesaw, initialize the core Matter.js modules: the engine, renderer, runner, and world composite.

const { Engine, Render, Runner, Bodies, Composite, Constraint, Mouse, MouseConstraint } = 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);
const runner = Runner.create();
Runner.run(runner, engine);

2. Creating the Seesaw Plank

The plank is a dynamic rectangular body. Its dimensions define the length and thickness of the teeter-totter.

const plankX = 400;
const plankY = 350;
const plankWidth = 400;
const plankHeight = 20;

const plank = Bodies.rectangle(plankX, plankY, plankWidth, plankHeight, {
    density: 0.002,
    friction: 0.5,
    render: {
        fillStyle: '#3498db'
    }
});

3. Adding the Central Pivot Constraint

To make the plank pivot around its center, attach it to a fixed point in the world using a revolute constraint. Setting length: 0 ensures the plank rotates around the anchor without sliding.

const pivot = Constraint.create({
    pointA: { x: plankX, y: plankY },
    bodyB: plank,
    pointB: { x: 0, y: 0 },
    stiffness: 1,
    length: 0,
    render: {
        visible: true,
        lineWidth: 3,
        strokeStyle: '#2c3e50'
    }
});

4. Positioning the Ground and Stoppers

Without stoppers, the plank will rotate 360 degrees when pushed. Place two static rectangular bodies underneath the ends of the plank to act as ground stops.

// Base floor
const ground = Bodies.rectangle(400, 580, 810, 40, { 
    isStatic: true,
    render: { fillStyle: '#7f8c8d' }
});

// Left and right stoppers
const stopperY = 400;
const stopperWidth = 30;
const stopperHeight = 80;

const leftStopper = Bodies.rectangle(plankX - 160, stopperY, stopperWidth, stopperHeight, {
    isStatic: true,
    render: { fillStyle: '#e74c3c' }
});

const rightStopper = Bodies.rectangle(plankX + 160, stopperY, stopperWidth, stopperHeight, {
    isStatic: true,
    render: { fillStyle: '#e74c3c' }
});

Adjusting the stopperY or the height of the stoppers directly controls the tilt limit of the seesaw.

5. Adding Interactivity and Objects

To interact with the seesaw, add dynamic test objects and enable mouse controls.

// Falling test weights
const weight1 = Bodies.circle(300, 100, 25, { density: 0.004, render: { fillStyle: '#f1c40f' } });
const weight2 = Bodies.rectangle(500, 100, 40, 40, { density: 0.002, render: { fillStyle: '#2ecc71' } });

// Mouse control
const mouse = Mouse.create(render.canvas);
const mouseConstraint = MouseConstraint.create(engine, {
    mouse: mouse,
    constraint: {
        stiffness: 0.2,
        render: { visible: false }
    }
});

// Add all bodies to the world
Composite.add(world, [
    plank, 
    pivot, 
    ground, 
    leftStopper, 
    rightStopper, 
    weight1, 
    weight2, 
    mouseConstraint
]);

render.mouse = mouse;

When you run this script, the weights land on opposite ends of the plank, tilting the structure until it makes physical contact with the static stoppers.