Collapsible Cardboard Box in Matter.js
This guide explains how to construct a collapsible 2D cardboard box using Matter.js that maintains its shape under normal conditions and flattens out when subjected to high impact. By assembling independent wall segments, linking them with pivot constraints, and maintaining their perpendicular angles with breakable structural supports, you can simulate realistic dynamic cardboard destruction in a web-based physics environment.
Core Concept
A 2D collapsible box requires four distinct rectangular bodies: a bottom base, a left wall, a right wall, and an optional top lid. To allow the box to flatten naturally upon impact:
- Hinge Joints: Corners must be connected with
permanent pin constraints (
length: 0) that allow rotation. - Angle Locks (Tape/Cardboard Rigidity): Diagonal or angular constraints bridge adjacent walls to hold the box in a rigid square.
- Break Condition: An update listener monitors stress, force, or collision impulses. When the threshold is exceeded, the angle locks are removed from the world, allowing the walls to fall flat under gravity.
Step 1: Create the Box Panels
Define the individual panels using
Matter.Bodies.rectangle. Adjust mass and friction to mimic
lightweight cardboard.
const { Bodies, Body, Composite, Constraint, Engine, Events } = Matter;
const wallThickness = 10;
const boxWidth = 120;
const boxHeight = 120;
const startX = 400;
const startY = 300;
// Base panel
const bottom = Bodies.rectangle(startX, startY + boxHeight / 2, boxWidth, wallThickness, {
collisionFilter: { group: -1 },
density: 0.002
});
// Left panel
const left = Bodies.rectangle(startX - boxWidth / 2, startY, wallThickness, boxHeight, {
collisionFilter: { group: -1 },
density: 0.002
});
// Right panel
const right = Bodies.rectangle(startX + boxWidth / 2, startY, wallThickness, boxHeight, {
collisionFilter: { group: -1 },
density: 0.002
});Assigning a negative collisionFilter.group prevents
self-intersection errors between adjacent panels at the joints while
still allowing collisions with external bodies.
Step 2: Assemble Hinge Joints
Create revolute joints at the bottom corners. These act as the cardboard creases that survive the collapse.
const hingeBottomLeft = Constraint.create({
bodyA: bottom,
pointA: { x: -boxWidth / 2, y: 0 },
bodyB: left,
pointB: { x: 0, y: boxHeight / 2 },
stiffness: 0.9,
length: 0
});
const hingeBottomRight = Constraint.create({
bodyA: bottom,
pointA: { x: boxWidth / 2, y: 0 },
bodyB: right,
pointB: { x: 0, y: boxHeight / 2 },
stiffness: 0.9,
length: 0
});Step 3: Add Breakable Structural Constraints
To keep the walls upright before impact, add diagonal cross-braces between the walls and base. Label these constraints so they can be targeted for removal.
const braceLeft = Constraint.create({
bodyA: bottom,
pointA: { x: -boxWidth / 4, y: 0 },
bodyB: left,
pointB: { x: 0, y: -boxHeight / 4 },
stiffness: 0.8,
label: "breakable"
});
const braceRight = Constraint.create({
bodyA: bottom,
pointA: { x: boxWidth / 4, y: 0 },
bodyB: right,
pointB: { x: 0, y: -boxHeight / 4 },
stiffness: 0.8,
label: "breakable"
});Step 4: Implement the Break Logic
Matter.js does not calculate breaking stress natively, so monitor
constraint deformation or listen for strong collision events using
Events.on(engine, 'afterUpdate', callback).
Measure the difference between a constraint's current world-space
length and its resting length. When the tension or
compression crosses your threshold, remove the brace from the physics
world:
const STRESS_LIMIT = 15; // Maximum allowable elongation in pixels
Events.on(engine, 'afterUpdate', () => {
const allConstraints = Composite.allConstraints(world);
allConstraints.forEach(constraint => {
if (constraint.label === "breakable") {
// Calculate current distance between attachment points
const posA = constraint.bodyA
? { x: constraint.bodyA.position.x + constraint.pointA.x, y: constraint.bodyA.position.y + constraint.pointA.y }
: constraint.pointA;
const posB = constraint.bodyB
? { x: constraint.bodyB.position.x + constraint.pointB.x, y: constraint.bodyB.position.y + constraint.pointB.y }
: constraint.pointB;
const currentDist = Math.hypot(posA.x - posB.x, posA.y - posB.y);
const deviation = Math.abs(currentDist - constraint.length);
if (deviation > STRESS_LIMIT) {
Composite.remove(world, constraint);
}
}
});
});Step 5: Add All Elements to the Engine
Add the bodies and constraints to the Composite:
Composite.add(world, [
bottom,
left,
right,
hingeBottomLeft,
hingeBottomRight,
braceLeft,
braceRight
]);Once external objects drop onto the box or drive high acceleration
through it, the diagonal braces stretch beyond STRESS_LIMIT
and delete themselves. Gravity immediately pulls the unbraced vertical
panels downward around their bottom hinges, causing the structure to
fold completely flat against the ground.