Update Body Vertices in Real-Time in Matter.js

This guide explains how to dynamically update and mutate the vertices of a rigid body in Matter.js during runtime. By using the engine's built-in Body.setVertices method, developers can reshape bodies, simulate mesh deformations, or create fluid geometric transformations without completely destroying and recreating physics instances. The following sections cover the core function, handling centroid shifts, dealing with concave shapes, and a practical implementation example.

Using Body.setVertices

The primary way to update a body's geometry in real time is through the Matter.Body.setVertices utility method. When invoked, Matter.js replaces the body's vertex array, recalculates its area, mass, moment of inertia, and updates its axis-aligned bounding box (AABB).

Matter.Body.setVertices(body, newVertices);

The newVertices parameter must be an array of vector-like objects containing x and y coordinates:

const newVertices = [
  { x: 0, y: 0 },
  { x: 100, y: 0 },
  { x: 100, y: 50 },
  { x: 0, y: 50 }
];

Handling the Centroid Offset

A common issue when updating vertices dynamically is the involuntary movement of the body. Matter.js automatically computes the geometric center (centroid) of the new vertices and moves the body's position to that centroid.

To preserve the body's perceived location in world space, store its original position prior to calling setVertices, or compute the delta between the old and new centroids and re-apply the desired position:

// Preserve the current position
const currentPosition = { x: body.position.x, y: body.position.y };

// Apply new vertices
Matter.Body.setVertices(body, newVertices);

// Restore the intended world position
Matter.Body.setPosition(body, currentPosition);

Concave Shapes and Poly-Decomp

Matter.js physics calculations require convex polygons. If your real-time vertex modifications produce a concave polygon, Matter.js will attempt to decompose the shape into multiple convex parts using the external poly-decomp library.

  1. Ensure poly-decomp is loaded in your environment before initializing your engine:
    Matter.Common.setDecomp(window.decomp);
  2. If poly-decomp is unavailable, Matter.js falls back to using the convex hull of the vertices, ignoring internal angles.

Complete Implementation Example

Below is a complete loop implementation that updates a body's vertices on every engine tick, oscillating a triangle's top vertex back and forth:

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

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

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

// Create an initial triangular body
const dynamicBody = Bodies.fromVertices(400, 300, [
  { x: 0, y: 100 },
  { x: 100, y: 100 },
  { x: 50, y: 0 }
], { isStatic: true });

Composite.add(world, dynamicBody);

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

// Real-time update loop
let angle = 0;

Matter.Events.on(engine, 'beforeUpdate', () => {
  angle += 0.05;
  const horizontalOffset = Math.sin(angle) * 40;

  // Compute updated vertex coordinates relative to the shape
  const updatedVertices = [
    { x: 0, y: 100 },
    { x: 100, y: 100 },
    { x: 50 + horizontalOffset, y: 0 } // Move top point dynamically
  ];

  const originalPosition = { x: dynamicBody.position.x, y: dynamicBody.position.y };

  // Apply new geometry
  Body.setVertices(dynamicBody, updatedVertices);

  // Maintain position stability
  Body.setPosition(dynamicBody, originalPosition);
});

Performance Considerations

Recalculating vertex hulls, mass properties, and decomposition routines is computationally expensive. When updating vertices in real time: