setup()
setup() runs once when the sketch starts. It initializes the Matter.js engine, builds the cloth particle grid, creates all the springs that hold it together, adds a ground floor, and sets up mouse interaction. This function demonstrates nested loops for building 2D grids, constraint creation for soft-body physics, and Matter.js API patterns you'll use in any physics-based sketch.
🔬 This line decides which particles are anchored. Currently only the two top corners are static. What happens if you change it to j === 0 (making the entire top row static) or remove the (i === 0 || i === numCols - 1) check? How does it change where the cloth can move?
// Make the top-left and top-right particles static (fixed)
const isStatic = (j === 0 && (i === 0 || i === numCols - 1));
function setup() {
createCanvas(windowWidth, windowHeight);
// Tip: Use windowWidth/windowHeight for responsive canvas
// Initialize Matter.js engine and world
engine = Engine.create();
world = engine.world;
world.gravity.y = 1; // Standard gravity
// Create the cloth particles
for (let j = 0; j < numRows; j++) {
cloth[j] = [];
for (let i = 0; i < numCols; i++) {
const x = (width / 2) - ((numCols * particleSpacing) / 2) + i * particleSpacing;
const y = 50 + j * particleSpacing; // Start cloth higher up
// Make the top-left and top-right particles static (fixed)
const isStatic = (j === 0 && (i === 0 || i === numCols - 1));
// Create a particle (circle body)
const particle = Bodies.circle(x, y, particleRadius, {
isStatic: isStatic,
friction: 0.1,
restitution: 0.2,
density: 0.001 // Make particles light
});
World.add(world, particle);
cloth[j][i] = particle;
// Create constraints (springs) between particles
if (i > 0) {
// Horizontal constraint
const hConstraint = Constraint.create({
bodyA: particle,
bodyB: cloth[j][i - 1],
length: particleSpacing,
stiffness: stiffness,
damping: damping
});
World.add(world, hConstraint);
constraints.push(hConstraint);
}
if (j > 0) {
// Vertical constraint
const vConstraint = Constraint.create({
bodyA: particle,
bodyB: cloth[j - 1][i],
length: particleSpacing,
stiffness: stiffness,
damping: damping
});
World.add(world, vConstraint);
constraints.push(vConstraint);
}
// Add diagonal constraints for better stability (optional)
if (i > 0 && j > 0) {
const diagLength = dist(0, 0, particleSpacing, particleSpacing);
// Diagonal top-left
const diag1 = Constraint.create({
bodyA: particle,
bodyB: cloth[j - 1][i - 1],
length: diagLength,
stiffness: stiffness,
damping: damping
});
World.add(world, diag1);
constraints.push(diag1);
// Diagonal top-right (only if not on right edge)
if (i < numCols - 1) {
const diag2 = Constraint.create({
bodyA: particle,
bodyB: cloth[j - 1][i + 1],
length: diagLength,
stiffness: stiffness,
damping: damping
});
World.add(world, diag2);
constraints.push(diag2);
}
}
}
}
// Create a ground body
const ground = Bodies.rectangle(width / 2, height - 10, width, 20, { isStatic: true });
World.add(world, ground);
// Add mouse interaction
// FIX: Revert to the original, correct syntax for Matter.js v0.20.0.
// Matter.js 0.20.0 expects the HTML element directly as the argument for Mouse.create().
const canvasMouse = Mouse.create(canvas.elt);
canvasMouse.pixelRatio = pixelDensity(); // Adjust for high-DPI screens
mouseConstraint = MouseConstraint.create(engine, {
mouse: canvasMouse,
constraint: {
stiffness: 0.2, // How strong the mouse drag is
render: {
visible: false // Don't draw the mouse constraint line
}
}
});
World.add(world, mouseConstraint);
}
Line-by-line explanation (19 lines)
🔧 Subcomponents:
for (let j = 0; j < numRows; j++) { ... for (let i = 0; i < numCols; i++) { ... } }
Builds a 2D grid of particles (rows × columns) positioned and spaced evenly across the canvas
const particle = Bodies.circle(x, y, particleRadius, { isStatic, friction, restitution, density });
Creates a Matter.js circle body with physics properties; marks top corners as static to anchor the cloth
if (i > 0) { ... } if (j > 0) { ... } if (i > 0 && j > 0) { ... }
Builds horizontal, vertical, and diagonal springs linking particles together to form a connected mesh
const canvasMouse = Mouse.create(canvas.elt); ... mouseConstraint = MouseConstraint.create(engine, { ... });
Configures Matter.js mouse tracking so dragging the canvas pulls particles toward your cursor
createCanvas(windowWidth, windowHeight);- Creates a canvas that fills the entire browser window, making the simulation responsive to screen size
engine = Engine.create();- Creates a Matter.js physics engine that will calculate forces, collisions, and constraint resolution every frame
world = engine.world;- Gets a reference to the physics world (the container holding all bodies and constraints)
world.gravity.y = 1;- Sets gravity strength—a positive value pulls particles downward, creating the hanging-cloth effect
for (let j = 0; j < numRows; j++) {- Outer loop iterates through each row (top to bottom) of the cloth grid
for (let i = 0; i < numCols; i++) {- Inner loop iterates through each column (left to right) for the current row, creating a 2D grid
const x = (width / 2) - ((numCols * particleSpacing) / 2) + i * particleSpacing;- Calculates the x position of each particle so the cloth is centered horizontally on the canvas
const y = 50 + j * particleSpacing;- Calculates the y position (starting 50 pixels from the top) and increasing downward with each row
const isStatic = (j === 0 && (i === 0 || i === numCols - 1));- Marks only the two top-corner particles as static (unmovable), so they anchor the cloth in place
const particle = Bodies.circle(x, y, particleRadius, { isStatic: isStatic, friction: 0.1, restitution: 0.2, density: 0.001 });- Creates a circular physics body at position (x, y) with properties that control how it bounces and drags through the air
World.add(world, particle);- Adds the newly created particle to the physics world so it is simulated and can collide/interact
cloth[j][i] = particle;- Stores the particle in a 2D array so we can reference it later when creating springs and drawing
if (i > 0) { const hConstraint = Constraint.create({ bodyA: particle, bodyB: cloth[j][i - 1], ... }); }- Creates a horizontal spring between the current particle and its left neighbor, pulling them toward particleSpacing distance apart
if (j > 0) { const vConstraint = Constraint.create({ bodyA: particle, bodyB: cloth[j - 1][i], ... }); }- Creates a vertical spring between the current particle and the one above it, maintaining vertical structure
if (i > 0 && j > 0) { const diagLength = dist(0, 0, particleSpacing, particleSpacing); ... }- Adds diagonal springs to prevent shearing—these springs preserve the cloth's grid structure and prevent collapse
const ground = Bodies.rectangle(width / 2, height - 10, width, 20, { isStatic: true });- Creates an invisible static floor at the bottom of the canvas to catch the cloth and prevent it from falling infinitely
const canvasMouse = Mouse.create(canvas.elt);- Tells Matter.js to track your mouse position on the p5 canvas element, converting screen coordinates to physics coordinates
canvasMouse.pixelRatio = pixelDensity();- Adjusts mouse tracking for high-resolution displays (like Retina screens) so dragging feels accurate
mouseConstraint = MouseConstraint.create(engine, { mouse: canvasMouse, constraint: { stiffness: 0.2, ... } });- Creates a temporary spring between your mouse and whichever particle you click, letting you drag the cloth interactively