Simulating Vocal Cord Mucosal Waves in Matter.js

This article explains how to simulate the biomechanical mucosal wave of vocal fold vibration using Matter.js, a 2D physics engine. By combining a multi-mass spring-damper model with dynamic aerodynamic forces derived from the myoelastic-aerodynamic theory of phonation, developers can recreate the phase-delayed, self-oscillating vertical wave motion characteristic of human vocal folds.

The Biomechanics of the Mucosal Wave

The mucosal wave is a surface wave that travels vertically from the inferior (bottom) margin to the superior (top) margin of the vocal folds during phonation. It is driven by two interdependent forces:

  1. Tissue Elasticity: The viscoelastic tissue layers (cover, transition, body) that resist displacement and return the vocal folds to equilibrium.
  2. Aerodynamics (The Bernoulli Effect): Subglottic pressure forces the folds apart from bottom to top. As air rushes through the narrow glottal constriction, pressure drops, pulling the lower margins back together while the upper margins are still opening.

Because Matter.js is a rigid-body physics engine rather than a fluid-structure interaction simulator, this mechanism is approximated using a discretized two-mass or multi-mass model driven by custom force calculations applied per frame.


1. Setting Up the Multi-Mass Model

To capture the out-of-phase vertical motion, each vocal fold (left and right) must be split into at least two vertically stacked horizontal masses: a lower mass (\(m_1\)) and an upper mass (\(m_2\)).

const { Engine, World, Bodies, Constraint, Body, Vector } = Matter;

const engine = Engine.create();
engine.gravity.scale = 0; // Disable default gravity to isolate aerodynamic forces

// Geometry parameters
const massWidth = 40;
const massHeight = 15;
const foldSeparation = 10; // Neutral glottal gap

// Left vocal fold masses
const leftLower = Bodies.rectangle(180, 215, massWidth, massHeight, { frictionAir: 0.05 });
const leftUpper = Bodies.rectangle(180, 195, massWidth, massHeight, { frictionAir: 0.05 });

// Right vocal fold masses
const rightLower = Bodies.rectangle(220 + foldSeparation, 215, massWidth, massHeight, { frictionAir: 0.05 });
const rightUpper = Bodies.rectangle(220 + foldSeparation, 195, massWidth, massHeight, { frictionAir: 0.05 });

World.add(engine.world, [leftLower, leftUpper, rightLower, rightUpper]);

2. Defining Viscoelastic Tissue Constraints

Link each mass to an anatomical anchor point (representing cartilage) with lateral spring-dampers. Crucially, connect the lower mass to the upper mass using a vertical shear spring. This shear constraint propagates energy and creates the vertical phase lag responsible for the mucosal wave.

// Anchor coordinates
const leftAnchorX = 120;
const rightAnchorX = 300;

// Lateral tissue elasticity
const leftLowerSpring = Constraint.create({
    pointA: { x: leftAnchorX, y: 215 },
    bodyB: leftLower,
    stiffness: 0.08,
    damping: 0.01
});

const leftUpperSpring = Constraint.create({
    pointA: { x: leftAnchorX, y: 195 },
    bodyB: leftUpper,
    stiffness: 0.06, // Upper cover is typically more pliable
    damping: 0.01
});

// Vertical shear coupling (mediates the mucosal wave)
const leftShearSpring = Constraint.create({
    bodyA: leftLower,
    bodyB: leftUpper,
    stiffness: 0.04,
    damping: 0.02
});

// Duplicate mirrored constraints for the right vocal fold
const rightLowerSpring = Constraint.create({ pointA: { x: rightAnchorX, y: 215 }, bodyB: rightLower, stiffness: 0.08, damping: 0.01 });
const rightUpperSpring = Constraint.create({ pointA: { x: rightAnchorX, y: 195 }, bodyB: rightUpper, stiffness: 0.06, damping: 0.01 });
const rightShearSpring = Constraint.create({ bodyA: rightLower, bodyB: rightUpper, stiffness: 0.04, damping: 0.02 });

World.add(engine.world, [
    leftLowerSpring, leftUpperSpring, leftShearSpring,
    rightLowerSpring, rightUpperSpring, rightShearSpring
]);

3. Implementing Aerodynamic Pressure Loops

The mucosal wave cannot sustain itself without energy input from lung pressure (\(P_{sub}\)). On every engine update, measure the instantaneous glottal width at the lower and upper apertures and compute the resulting aerodynamic forces:

  1. Convergent Shape (Lower gap > Upper gap): High intraglottal pressure pushes the walls outward.
  2. Divergent Shape (Lower gap < Upper gap): Air separates from the walls, producing negative Bernoulli pressure that pulls the folds inward.
  3. Collision / Closure: When the gap is zero, collision response halts inward momentum, and subglottal pressure builds up underneath.
const P_SUBGLOTTAL = 1.2; // Driving lung pressure

Matter.Events.on(engine, 'beforeUpdate', () => {
    // Calculate instantaneous apertures
    const lowerGap = Math.max(0.1, rightLower.position.x - leftLower.position.x - massWidth);
    const upperGap = Math.max(0.1, rightUpper.position.x - leftUpper.position.x - massWidth);

    // Bernoulli-driven intraglottal pressures
    let lowerPressure, upperPressure;

    if (lowerGap <= 1.0) {
        // Glottal closure: full subglottic pressure forces lower masses apart
        lowerPressure = P_SUBGLOTTAL;
        upperPressure = 0;
    } else {
        // Open glottis: pressure distribution depends on profile convergence/divergence
        const areaRatio = lowerGap / upperGap;

        if (areaRatio >= 1.0) {
            // Convergent profile: positive driving pressure
            lowerPressure = P_SUBGLOTTAL * 0.7;
            upperPressure = P_SUBGLOTTAL * 0.3;
        } else {
            // Divergent profile: negative Bernoulli suction pulls upper/lower masses together
            lowerPressure = P_SUBGLOTTAL * 0.1;
            upperPressure = -P_SUBGLOTTAL * 0.4;
        }
    }

    // Apply forces laterally to simulate pressure
    Body.applyForce(leftLower, leftLower.position, { x: -lowerPressure * 0.05, y: 0 });
    Body.applyForce(rightLower, rightLower.position, { x: lowerPressure * 0.05, y: 0 });

    Body.applyForce(leftUpper, leftUpper.position, { x: -upperPressure * 0.05, y: 0 });
    Body.applyForce(rightUpper, rightUpper.position, { x: upperPressure * 0.05, y: 0 });
});

4. Tuning and Observing the Wave

To maintain stable self-oscillation and visually confirm the mucosal wave:

When tuned correctly, plotting the horizontal positions of leftLower and leftUpper over time will reveal matching sinusoidal waveforms where leftUpper consistently lags behind leftLower by approximately \(60^\circ\) to \(90^\circ\), reproducing the mucosal wave.