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:

  1. Hinge Joints: Corners must be connected with permanent pin constraints (length: 0) that allow rotation.
  2. Angle Locks (Tape/Cardboard Rigidity): Diagonal or angular constraints bridge adjacent walls to hold the box in a rigid square.
  3. 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.