setup()
setup() runs once when the sketch starts. It initializes the Matter.js physics engine, creates all the static and dynamic bodies that make up the machine, and connects bodies with constraints to create moveable joints. Understanding each Bodies.rectangle() and Bodies.circle() call teaches you how to build complex physical structures by combining simple shapes.
🔬 These four bodies form the containing box. What happens if you comment out the ceiling line (add // before let ceiling)? How does gravity behave?
let ground = Bodies.rectangle(width / 2, height - 10, width, 20, { isStatic: true });
let leftWall = Bodies.rectangle(10, height / 2, 20, height, { isStatic: true });
let rightWall = Bodies.rectangle(width - 10, height / 2, 20, height, { isStatic: true });
let ceiling = Bodies.rectangle(width / 2, 10, width, 20, { isStatic: true });
🔬 This loop creates 5 dominoes. What happens if you change the 5 to 10? To 2? How does the cascade length change?
for (let i = 0; i < 5; i++) {
let domino = Bodies.rectangle(startX1 + i * dominoSpacing, startY1, dominoWidth, dominoHeight, { friction: 0.5, restitution: 0.1 });
dominoes1.push(domino);
}
function setup() {
createCanvas(windowWidth, windowHeight * 1.8);
frameRate(30);
engine = Engine.create();
world = engine.world;
world.gravity.y = 1;
let ground = Bodies.rectangle(width / 2, height - 10, width, 20, { isStatic: true });
let leftWall = Bodies.rectangle(10, height / 2, 20, height, { isStatic: true });
let rightWall = Bodies.rectangle(width - 10, height / 2, 20, height, { isStatic: true });
let ceiling = Bodies.rectangle(width / 2, 10, width, 20, { isStatic: true });
World.add(world, [ground, leftWall, rightWall, ceiling]);
bodies.push(ground, leftWall, rightWall, ceiling);
startBall = Bodies.circle(width * 0.2, 50, 15, { friction: 0.1, restitution: 0.8, isStatic: true });
World.add(world, startBall);
bodies.push(startBall);
let ramp1 = Bodies.rectangle(width * 0.35, height * 0.1, 200, 20, { isStatic: true, angle: PI / 6 });
World.add(world, ramp1);
bodies.push(ramp1);
let seesawBlock = Bodies.rectangle(width * 0.6, height * 0.2, 250, 10);
let seesawPivot = Bodies.circle(width * 0.6, height * 0.2, 5, { isStatic: true });
let seesawConstraint = Constraint.create({
bodyA: seesawBlock,
pointB: { x: seesawBlock.position.x, y: seesawBlock.position.y },
pointA: { x: 0, y: 0 },
stiffness: 0.05
});
World.add(world, [seesawBlock, seesawPivot, seesawConstraint]);
bodies.push(seesawBlock, seesawPivot);
let seesawWeight = Bodies.rectangle(width * 0.75, height * 0.15, 30, 30, { friction: 0.1, restitution: 0.8 });
World.add(world, seesawWeight);
bodies.push(seesawWeight);
let dominoes1 = [];
let dominoWidth = 10;
let dominoHeight = 50;
let dominoSpacing = 20;
let startX1 = width * 0.2;
let startY1 = height * 0.3;
for (let i = 0; i < 5; i++) {
let domino = Bodies.rectangle(startX1 + i * dominoSpacing, startY1, dominoWidth, dominoHeight, { friction: 0.5, restitution: 0.1 });
dominoes1.push(domino);
}
World.add(world, dominoes1);
bodies.push(...dominoes1);
let leverBlock = Bodies.rectangle(width * 0.45, height * 0.4, 150, 10, { isStatic: true, angle: -PI / 8 });
World.add(world, leverBlock);
bodies.push(leverBlock);
let finalBall1 = Bodies.circle(width * 0.55, height * 0.35, 15, { friction: 0.1, restitution: 0.8 });
World.add(world, finalBall1);
bodies.push(finalBall1);
let bucketWidth = 100;
let bucketHeight = 70;
let bucketX = width * 0.8;
let bucketY = height * 0.5;
let bucketBottom = Bodies.rectangle(bucketX, bucketY + bucketHeight / 2, bucketWidth, 10, { isStatic: true });
let bucketLeft = Bodies.rectangle(bucketX - bucketWidth / 2 + 5, bucketY, 10, bucketHeight, { isStatic: true });
let bucketRight = Bodies.rectangle(bucketX + bucketWidth / 2 - 5, bucketY, 10, bucketHeight, { isStatic: true });
let bucket = Composite.create({
bodies: [bucketBottom, bucketLeft, bucketRight]
});
World.add(world, bucket);
bodies.push(bucketBottom, bucketLeft, bucketRight);
let seesawLaunchPivot = Bodies.circle(width * 0.7, height * 0.65, 5, { isStatic: true });
let seesawLaunchBlock = Bodies.rectangle(width * 0.7, height * 0.65, 200, 10);
let seesawLaunchConstraint = Constraint.create({
bodyA: seesawLaunchBlock,
pointB: { x: seesawLaunchBlock.position.x, y: seesawLaunchBlock.position.y },
pointA: { x: 0, y: 0 },
stiffness: 0.05
});
World.add(world, [seesawLaunchPivot, seesawLaunchBlock, seesawLaunchConstraint]);
bodies.push(seesawLaunchPivot, seesawLaunchBlock);
let ballToLaunch = Bodies.circle(width * 0.85, height * 0.6, 15, { friction: 0.1, restitution: 0.8 });
World.add(world, ballToLaunch);
bodies.push(ballToLaunch);
let funnelLeft = Bodies.rectangle(width * 0.6, height * 0.8, 10, 80, { isStatic: true, angle: -PI / 8 });
let funnelRight = Bodies.rectangle(width * 0.7, height * 0.8, 10, 80, { isStatic: true, angle: PI / 8 });
World.add(world, [funnelLeft, funnelRight]);
bodies.push(funnelLeft, funnelRight);
let slide2 = Bodies.rectangle(width * 0.45, height * 0.85, 250, 10, { isStatic: true, angle: PI / 12 });
World.add(world, slide2);
bodies.push(slide2);
let pendulumPivot = Bodies.circle(width * 0.2, height * 0.9, 5, { isStatic: true });
let pendulumBob = Bodies.circle(width * 0.2, height * 1.05, 20, { friction: 0.1, restitution: 0.8 });
let pendulumConstraint = Constraint.create({
bodyA: pendulumPivot,
pointB: { x: pendulumBob.position.x, y: pendulumBob.position.y },
pointA: { x: 0, y: -100 },
stiffness: 0.005,
damping: 0.01
});
World.add(world, [pendulumPivot, pendulumBob, pendulumConstraint]);
bodies.push(pendulumPivot, pendulumBob);
let pendulumTriggerBlock = Bodies.rectangle(width * 0.3, height * 1.05, 40, 40, { friction: 0.1, restitution: 0.8 });
World.add(world, pendulumTriggerBlock);
bodies.push(pendulumTriggerBlock);
let dominoes2 = [];
let startX2 = width * 0.4;
let startY2 = height * 1.1;
for (let i = 0; i < 7; i++) {
let domino = Bodies.rectangle(startX2 + i * dominoSpacing, startY2, dominoWidth, dominoHeight, { friction: 0.5, restitution: 0.1 });
dominoes2.push(domino);
}
World.add(world, dominoes2);
bodies.push(...dominoes2);
let finalTarget = Bodies.rectangle(width * 0.7, height * 1.25, 80, 80, { isStatic: true, friction: 0.1, restitution: 0.8 });
World.add(world, finalTarget);
bodies.push(finalTarget);
for (let i = 0; i < 2; i++) {
let x = random(width * 0.1, width * 0.9);
let y = random(height * 0.1, height * 0.9);
let size = random(10, 40);
let body;
if (random() > 0.5) {
body = Bodies.circle(x, y, size / 2, { friction: 0.1, restitution: 0.8 });
} else {
body = Bodies.rectangle(x, y, size, size, { friction: 0.1, restitution: 0.8 });
}
World.add(world, body);
bodies.push(body);
}
}
Line-by-line explanation (12 lines)
🔧 Subcomponents:
engine = Engine.create(); world = engine.world; world.gravity.y = 1;
Creates the Matter.js physics engine and sets gravity to pull all objects downward
let ground = Bodies.rectangle(width / 2, height - 10, width, 20, { isStatic: true });
Creates immovable walls and ground that contain all dynamic bodies within the canvas
startBall = Bodies.circle(width * 0.2, 50, 15, { friction: 0.1, restitution: 0.8, isStatic: true });
Creates the initial ball at top-left, marked as static so it waits for a click to become dynamic
let ramp1 = Bodies.rectangle(width * 0.35, height * 0.1, 200, 20, { isStatic: true, angle: PI / 6 });
Creates a tilted static surface to guide the ball downward and accelerate it
let seesawConstraint = Constraint.create({ bodyA: seesawBlock, pointB: { x: seesawBlock.position.x, y: seesawBlock.position.y }, pointA: { x: 0, y: 0 }, stiffness: 0.05 });
Connects the seesaw block to a pivot point with a constraint, allowing it to rotate like a real seesaw
for (let i = 0; i < 5; i++) { let domino = Bodies.rectangle(startX1 + i * dominoSpacing, startY1, dominoWidth, dominoHeight, { friction: 0.5, restitution: 0.1 }); dominoes1.push(domino); }
Creates a row of 5 upright rectangles spaced evenly so they topple in sequence when struck
let bucket = Composite.create({ bodies: [bucketBottom, bucketLeft, bucketRight] });
Groups three static rectangles into one composite to act as a container that catches falling balls
let pendulumConstraint = Constraint.create({ bodyA: pendulumPivot, pointB: { x: pendulumBob.position.x, y: pendulumBob.position.y }, pointA: { x: 0, y: -100 }, stiffness: 0.005, damping: 0.01 });
Creates a swinging pendulum bob connected to a fixed pivot with low stiffness for realistic swing motion
createCanvas(windowWidth, windowHeight * 1.8);- Creates a canvas 80% taller than the window height to accommodate the extended 11-stage machine vertically
frameRate(30);- Locks the animation to 30 frames per second for smoother physics simulation and consistent behavior
engine = Engine.create();- Initializes the Matter.js physics engine—the core system that calculates motion, collisions, and forces
world = engine.world;- Retrieves the world object from the engine, which is where all bodies and constraints live
world.gravity.y = 1;- Sets the gravitational acceleration downward to 1 pixel/frame/frame, pulling all objects toward the bottom
let ground = Bodies.rectangle(width / 2, height - 10, width, 20, { isStatic: true });- Creates a thin static rectangle at the bottom of the canvas to act as immovable ground
World.add(world, [ground, leftWall, rightWall, ceiling]);- Adds all boundary bodies to the physics world so they interact with dynamic bodies
bodies.push(ground, leftWall, rightWall, ceiling);- Stores references to all boundary bodies in the global bodies array so draw() can render them each frame
startBall = Bodies.circle(width * 0.2, 50, 15, { friction: 0.1, restitution: 0.8, isStatic: true });- Creates the trigger ball as a circle with radius 15 at the top-left, marked static so it won't fall until clicked
for (let i = 0; i < 5; i++) {- Loops 5 times to create a row of dominoes at different horizontal positions
let domino = Bodies.rectangle(startX1 + i * dominoSpacing, startY1, dominoWidth, dominoHeight, { friction: 0.5, restitution: 0.1 });- Creates each domino as a thin (10px wide) tall rectangle spaced 20px apart horizontally
let seesawConstraint = Constraint.create({ bodyA: seesawBlock, pointB: { x: seesawBlock.position.x, y: seesawBlock.position.y }, pointA: { x: 0, y: 0 }, stiffness: 0.05 });- Creates a spring-like joint that connects the seesaw block to an invisible pivot point, allowing it to rotate and wobble