Simulate Torpedo and Hydrofoil Physics in Matter.js

Simulating an underwater torpedo with hydrofoil steering in Matter.js requires modeling three core physical phenomena: constant axial thrust (propulsion), anisotropic fluid drag (water resistance that permits forward motion while resisting sideways slippage), and dynamic hydrodynamic lift generated by steering hydrofoils. Because Matter.js is fundamentally a rigid-body physics engine built for dry environments, this guide details how to implement a custom update loop that calculates and applies these fluid forces to a body in real time.

1. Environment and Body Configuration

Water cancels out most or all of an object's weight through buoyancy. In Matter.js, set the world's vertical gravity to zero, or calculate an upward buoyant force equal to gravity for a neutrally buoyant vessel.

Initialize the torpedo as a rectangular or chamfered rigid body with realistic physical properties:

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

// Create engine and disable global gravity to mimic neutral buoyancy
const engine = Engine.create({
  gravity: { x: 0, y: 0, scale: 0 }
});

// Create the torpedo body
const torpedo = Bodies.rectangle(400, 300, 120, 24, {
  density: 0.002,
  frictionAir: 0, // Disable default uniform air resistance
  friction: 0.1,
  restitution: 0.1
});

Composite.add(engine.world, torpedo);

2. Forward Propulsion (Thrust)

A torpedo's propeller provides continuous thrust along its longitudinal axis. To compute this, extract the forward directional vector from the torpedo's current rotation angle and scale it by the desired engine power.

function applyThrust(body, thrustPower) {
  // Vector pointing along the torpedo's forward axis
  const forwardVector = {
    x: Math.cos(body.angle),
    y: Math.sin(body.angle)
  };

  const force = Vector.mult(forwardVector, thrustPower);
  Body.applyForce(body, body.position, force);
}

3. Hydrodynamic Drag (Water Resistance)

Uniform linear damping (such as the default frictionAir) makes a torpedo feel like it is floating in air rather than slicing through water. Torpedoes encounter low drag along their streamlined longitudinal axis and massive drag perpendicular to their hull (lateral drag).

Deconstruct the body's velocity into forward and lateral components, apply separate drag coefficients to each, and damp the angular velocity:

function applyHydrodynamicDrag(body) {
  const forwardVector = { x: Math.cos(body.angle), y: Math.sin(body.angle) };
  const lateralVector = { x: -Math.sin(body.angle), y: Math.cos(body.angle) };

  // Project current velocity onto forward and lateral axes
  const forwardSpeed = Vector.dot(body.velocity, forwardVector);
  const lateralSpeed = Vector.dot(body.velocity, lateralVector);

  // Drag coefficients (drag increases quadratically or linearly with speed)
  const forwardDragCoeff = 0.001;
  const lateralDragCoeff = 0.05; // High resistance to sideways motion
  const angularDragCoeff = 0.08; // High resistance to spinning

  // Compute opposing drag forces
  const forwardDragForce = Vector.mult(forwardVector, -forwardSpeed * forwardDragCoeff * Math.abs(forwardSpeed));
  const lateralDragForce = Vector.mult(lateralVector, -lateralSpeed * lateralDragCoeff * Math.abs(lateralSpeed));

  const totalDragForce = Vector.add(forwardDragForce, lateralDragForce);

  // Apply translational and rotational damping
  Body.applyForce(body, body.position, totalDragForce);
  body.torque -= body.angularVelocity * angularDragCoeff * body.inertia;
}

4. Hydrofoil Steering (Lift Mechanics)

Hydrofoils (fins or rudders) steer a vessel by redirecting water flow, which generates a lift force perpendicular to the relative flow direction. Because Matter.js calculates rotation through torque:

  1. Calculate the effective flow speed: Steering foils only work when water is moving over them (forward velocity).
  2. Compute the angle of attack: The angle between the incoming flow and the hydrofoil surface.
  3. Apply steering torque: The torque must be proportional to the deflection of the rudder and the forward speed.
let rudderAngle = 0; // Negative for left, positive for right (in radians)

function applyHydrofoilSteering(body, rudderAngle) {
  const forwardVector = { x: Math.cos(body.angle), y: Math.sin(body.angle) };
  const forwardSpeed = Vector.dot(body.velocity, forwardVector);

  // Hydrofoils generate lift proportional to forward speed and fin deflection
  const liftCoefficient = 0.0005;
  const steeringTorque = rudderAngle * forwardSpeed * liftCoefficient * body.inertia;

  // Apply torque to change the heading
  body.torque += steeringTorque;
}

5. Running the Physics Loop

Hook the force calculations into Matter.js's beforeUpdate event. This ensures all fluid forces, propulsion vectors, and hydrofoil adjustments are resolved directly before collision detection and integration occur in each frame:

Matter.Events.on(engine, 'beforeUpdate', () => {
  const thrustMagnitude = 0.004;

  // 1. Push forward
  applyThrust(torpedo, thrustMagnitude);

  // 2. Dissipate lateral energy and spin
  applyHydrodynamicDrag(torpedo);

  // 3. Apply foil-induced turning based on user control
  applyHydrofoilSteering(torpedo, rudderAngle);
});

By balancing lateral drag and forward speed-dependent torque, the torpedo naturally follows curved trajectories without sliding sideways, accurately mirroring underwater fluid dynamics.