How to Simulate Acoustic Levitation in Matter.js

This article explains how to simulate acoustic levitation in the Matter.js 2D physics engine by modeling acoustic radiation forces and standing wave nodes. By calculating Gor'kov-inspired restoring forces and applying them iteratively to disc bodies via the engine's update cycle, you can trap, stabilize, and suspend small particles between simulated ultrasonic transducers against gravity.

Fundamentals of the Acoustic Model

Acoustic levitation relies on standing acoustic waves formed between an emitter and a reflector (or opposing emitters). In a 1D vertical standing wave, the acoustic radiation force \(F_{\text{rad}}\) acting on small particles can be approximated as a sinusoidal force function:

\[F_{\text{rad}} = -F_{\text{max}} \sin(2 k y)\]

Where:

Stable pressure nodes occur at intervals of \(\frac{\lambda}{2}\). Particles denser than the medium are pushed away from antinodes and become trapped at these nodal points.

Setting Up Matter.js

To set up the simulation, initialize the standard Matter.js modules and construct the world. Gravity can be left at its default downward value (\(g = 1\)) to demonstrate the acoustic force counteracting gravity, or reduced for easier calibration.

const { Engine, Render, Runner, Bodies, Composite, Events, Body, Vector } = Matter;

const engine = Engine.create();
const world = engine.world;

// Standard downward gravity
engine.gravity.y = 1;

const render = Render.create({
    element: document.body,
    engine: engine,
    options: {
        width: 800,
        height: 600,
        wireframes: false
    }
});

Render.run(render);
const runner = Runner.create();
Runner.run(runner, engine);

Creating the Acoustic Field Parameters

Define the wavelength and field strength. The wavelength sets the spacing between the levitation nodes, while the maximum force determines the trap's stiffness.

const WAVELENGTH = 80; // Distance in pixels
const K = (2 * Math.PI) / WAVELENGTH;
const F_MAX = 0.005; // Peak acoustic force
const LATERAL_STIFFNESS = 0.0001; // Restores particles to the center axis
const FIELD_CENTER_X = 400;

Spawning Disc Bodies

Create small disc bodies using Bodies.circle. Assign a low friction and an appropriate mass so that the calculated force can overcome gravity.

const discs = [];
const discRadius = 8;

for (let i = 0; i < 5; i++) {
    const disc = Bodies.circle(FIELD_CENTER_X + (Math.random() - 0.5) * 20, 100 + i * 40, discRadius, {
        frictionAir: 0.05, // High air resistance aids stabilization
        restitution: 0.2,
        render: { fillStyle: '#4CAF50' }
    });
    discs.push(disc);
    Composite.add(world, disc);
}

Applying Acoustic Forces

Use the beforeUpdate event to apply forces before every physics step. The applied force contains two components:

  1. Vertical Force (\(F_y\)): The primary standing-wave force counteracting gravity and pulling particles to nodes.
  2. Horizontal Force (\(F_x\)): A lateral confining force that simulates the acoustic beam's focal waist, preventing particles from slipping out sideways.
Events.on(engine, 'beforeUpdate', () => {
    discs.forEach(disc => {
        const y = disc.position.y;
        const x = disc.position.x;

        // Vertical standing wave force: oscillates between nodes
        const fy = -F_MAX * Math.sin(2 * K * y);

        // Horizontal confining force toward the beam center
        const fx = -LATERAL_STIFFNESS * (x - FIELD_CENTER_X);

        // Apply force directly to the body center of mass
        Body.applyForce(disc, disc.position, { x: fx, y: fy });
    });
});

Tuning for Stability

Simulating acoustic levitation requires balancing three factors: