Simulating a Mousetrap Snap in Matter.js
This guide explains how to simulate the rapid, violent snapping motion of a mechanical mousetrap in Matter.js using high-torque spring constraints. By anchoring a dynamic lever arm to a static base with a revolute pivot and applying an offset, high-stiffness distance constraint, you can generate the angular acceleration necessary to mimic a spring-loaded trap. The following sections break down the physical architecture, constraint configurations, stability adjustments, and the release mechanism.
1. Setting Up the Physical Bodies
A mousetrap requires two primary bodies: a static base and a dynamic lever arm (the striker or hammer).
const { Engine, Render, Runner, Bodies, Composite, Constraint } = Matter;
const engine = Engine.create();
const world = engine.world;
// Mousetrap base
const base = Bodies.rectangle(400, 500, 200, 20, {
isStatic: true
});
// Striking arm
const armLength = 120;
const arm = Bodies.rectangle(460, 485, armLength, 10, {
density: 0.005,
collisionFilter: { group: -1 } // Prevent collision with the base pivot
});
Composite.add(world, [base, arm]);2. Establishing the Fulcrum Pivot
Matter.js does not have a native "revolute joint" object, but you can create a hinge by using a zero-length constraint that connects the arm to the base at a shared pivot point:
const pivot = Constraint.create({
bodyA: base,
pointA: { x: -60, y: -10 },
bodyB: arm,
pointB: { x: -armLength / 2, y: 0 },
length: 0,
stiffness: 1
});
Composite.add(world, pivot);3. Creating High-Torque Rotational Force
Because Matter.js does not provide a dedicated torsional spring constraint, rotational torque is achieved by connecting a linear constraint between an anchor point behind the fulcrum and an offset point on the lever arm.
Torque is the product of force and the perpendicular distance from the fulcrum. To maximize torque:
- Attach the spring to an offset point along the arm rather than the exact pivot center.
- Set the
lengthof the spring constraint to zero or a very small resting value. - Maximize
stiffness(approaching1.0) to create instantaneous acceleration.
const spring = Constraint.create({
bodyA: base,
pointA: { x: -80, y: -10 }, // Fixed anchor point on the base
bodyB: arm,
pointB: { x: -armLength / 2 + 15, y: -5 }, // Offset point on the arm
length: 0,
stiffness: 0.8,
damping: 0.05
});
Composite.add(world, spring);When the arm is rotated 180 degrees into the "set" position, the distance between these two attachment points stretches significantly, storing the potential energy required for the snap.
4. Holding and Triggering the Trap
To hold the trap under tension, define a temporary latch constraint or a static catch body that holds the arm in the cocked position. When triggered by a collision or a timer, remove the latch from the world.
// Latch holding the arm fully open at the opposite side of the base
const latch = Constraint.create({
bodyA: base,
pointA: { x: 70, y: -10 },
bodyB: arm,
pointB: { x: armLength / 2, y: 0 },
stiffness: 1,
length: 0
});
Composite.add(world, latch);
// Trigger: Release the latch to snap the trap shut
function triggerTrap() {
Composite.remove(world, latch);
}5. Managing Simulation Stability
High-velocity constraints can cause bodies to tunnel through barriers or violently oscillate upon impact. To ensure stability during the snap:
- Increase Iterations: Raise
engine.positionIterationsandengine.velocityIterationsto at least10or12in the engine configuration to prevent joint separation during sudden velocity spikes. - Add Restitution and Damping: Keep the arm's
restitution(bounciness) low (around0.1to0.2) and tune the constraintdampingto allow the trap to slam shut without endless high-frequency jitter on the base. - Stop Block: Place a thin static body on the base where the arm is intended to land to physically arrest its motion, absorbing the angular momentum cleanly.