Simulate Arterial Pulse Waves in Matter.js
This article explains how to model arterial pulse wave propagation through elastic vessel walls using the Matter.js 2D physics engine. By representing vessel walls as connected chains of rigid bodies bound by elastic constraints and driving fluid pressure using dynamic particle clusters or localized force vectors, developers can simulate biomechanical wave mechanics, wall distension, and wave velocity in a real-time web environment.
Understanding the Mechanics in a Rigid-Body Engine
Arterial pulse wave propagation is a fluid-structure interaction (FSI) problem governed by the Moens-Korteweg equation, where pulse wave velocity (PWV) depends on vessel radius, wall thickness, blood density, and the elastic modulus of the arterial wall. Because Matter.js is a discrete 2D rigid-body engine rather than a continuous Navier-Stokes solver, the simulation relies on a lumped-parameter approach:
- The Elastic Vessel Wall: Modeled as a series of small, interconnected rectangular or circular rigid bodies linked by stiff spring constraints with damping.
- The Pulse/Fluid Medium: Modeled either as an ensemble of self-repelling circular rigid bodies (discrete fluid particles) or as direct radial force impulses applied progressively along the interior wall nodes.
Step 1: Constructing the Elastic Vessel Walls
To create the upper and lower arterial boundaries, construct two parallel chains of bodies connected by distance constraints.
const { Engine, Render, Runner, Bodies, Composite, Constraint, Vector } = Matter;
const engine = Engine.create({ gravity: { x: 0, y: 0 } });
const world = engine.world;
const segments = 40;
const segmentWidth = 20;
const segmentHeight = 8;
const vesselRadius = 60;
const startX = 100;
const centerY = 300;
const upperWall = [];
const lowerWall = [];
// Generate wall segments
for (let i = 0; i < segments; i++) {
const x = startX + i * segmentWidth;
const topBody = Bodies.rectangle(x, centerY - vesselRadius, segmentWidth, segmentHeight, {
collisionFilter: { group: -1 },
frictionAir: 0.05
});
const bottomBody = Bodies.rectangle(x, centerY + vesselRadius, segmentWidth, segmentHeight, {
collisionFilter: { group: -1 },
frictionAir: 0.05
});
upperWall.push(topBody);
lowerWall.push(bottomBody);
Composite.add(world, [topBody, bottomBody]);
}Step 2: Linking Segments with Elastic Constraints
Elasticity is defined by the stiffness and
damping of the constraints joining adjacent segments, as
well as structural anchoring constraints that simulate surrounding
tissue tethering.
// Link adjacent horizontal segments
for (let i = 0; i < segments - 1; i++) {
const topLink = Constraint.create({
bodyA: upperWall[i],
bodyB: upperWall[i + 1],
stiffness: 0.8,
damping: 0.1
});
const bottomLink = Constraint.create({
bodyA: lowerWall[i],
bodyB: lowerWall[i + 1],
stiffness: 0.8,
damping: 0.1
});
Composite.add(world, [topLink, bottomLink]);
}
// Tether walls to their resting position to simulate tissue elasticity
for (let i = 0; i < segments; i++) {
const topAnchor = Constraint.create({
bodyA: upperWall[i],
pointB: { x: upperWall[i].position.x, y: centerY - vesselRadius },
stiffness: 0.15,
damping: 0.05
});
const bottomAnchor = Constraint.create({
bodyA: lowerWall[i],
pointB: { x: lowerWall[i].position.x, y: centerY + vesselRadius },
stiffness: 0.15,
damping: 0.05
});
Composite.add(world, [topAnchor, bottomAnchor]);
}
// Pin the proximal and distal ends
upperWall[0].isStatic = true;
lowerWall[0].isStatic = true;
upperWall[segments - 1].isStatic = true;
lowerWall[segments - 1].isStatic = true;Step 3: Injecting the Pulse Wave
The cardiac ejection cycle generates a transient high-pressure wave. You can simulate this through dynamic pressure application at the proximal inlet:
Method A: Force-Vector Injection (Recommended for Stability)
Apply a localized, transient radial force to the first few wall segments, then allow the constraint network to naturally propagate the kinetic energy downstream.
function triggerSystolicPulse() {
const pulseMagnitude = 0.05;
// Apply outward vertical force to proximal segments
for (let i = 1; i <= 3; i++) {
Matter.Body.applyForce(upperWall[i], upperWall[i].position, { x: 0.01, y: -pulseMagnitude });
Matter.Body.applyForce(lowerWall[i], lowerWall[i].position, { x: 0.01, y: pulseMagnitude });
}
}
// Trigger pulse periodically (e.g., 60 BPM)
setInterval(triggerSystolicPulse, 1000);Method B: Particle-Based Hydraulic Fluid
Fill the lumen with high-density, low-friction circular bodies. A kinematic piston body at the inlet compresses the fluid particles forward, creating localized hydrostatic pressure that distends the adjacent walls. The wave propagates forward as particles collide and transfer momentum through the flexible tube.
Step 4: Calibrating Wave Speed and Damping
To achieve realistic wave propagation without excessive instability:
- Pulse Wave Velocity (PWV): Increase constraint
stiffnessto increase wave speed; decrease segment mass to increase acceleration. - Reflected Waves: In real physiology, pulse waves reflect off arterial bifurcations and peripheral resistance. You can simulate peripheral reflection by stiffening distal constraints or leaving the distal end partially clamped.
- Viscous Dissipation: Tune
frictionAiron the segment bodies anddampingon the constraints to emulate energy dissipation in vascular tissue. Too low a damping value will cause unrealistic sustained oscillations.