Matter.js Self-Intersecting Polygons: Effects and Fixes

Defining a self-intersecting polygon in Matter.js causes severe simulation glitches, including broken collision detection, erroneous mass properties, and failure during convex decomposition. Because Matter.js relies on algorithms that assume simple, non-self-intersecting geometric boundaries, feeding it intersecting vertices violates its mathematical preconditions. This article details the specific physics engine breakdowns that occur with self-intersecting paths and explains how to properly handle complex geometry in your simulations.

Broken Mass and Inertia Calculations

Matter.js calculates a body's physical properties—such as area, center of mass, and moment of inertia—using Green's theorem, which integrates over the boundary formed by the vertices. This calculation assumes that the vertex loop defines a clear interior and exterior with consistent winding (clockwise or counter-clockwise).

When edges intersect, parts of the polygon have opposing winding directions relative to each other. As a result:

Collision Failures with the Separating Axis Theorem

Matter.js relies on the Separating Axis Theorem (SAT) to detect collisions between rigid bodies. SAT strictly requires convex shapes. When an arbitrary polygon is provided directly via Matter.Body.create, the engine treats the vertices as a single hull.

With a self-intersecting polygon:

Failure in Bodies.fromVertices and Poly-Decomp

To handle non-convex shapes, Matter.js provides Matter.Bodies.fromVertices(), which integrates with the external library poly-decomp.js to automatically slice concave polygons into a set of convex parts.

poly-decomp.js requires simple polygons that do not cross over themselves. When passed a self-intersecting set of vertices:

Visual Artifacts in the Default Renderer

The built-in Matter.Render module renders vertices using the HTML5 Canvas 2D API's path drawing methods (moveTo and lineTo). By default, canvas rendering uses the non-zero winding rule to fill paths.

A self-intersecting polygon creates overlapping sub-regions where the fill rule produces visual inconsistencies:

How to Properly Handle Self-Intersecting Polygons

To use shapes with intersecting edges in Matter.js, you must preprocess the geometry before instantiating the physics body:

  1. Polygon Boolean Operations: Run the vertex set through a polygon-clipping library (such as polyclip-ts or martinez-polygon-clipping) to perform a self-union operation. This resolves self-intersections and extracts a clean, non-overlapping boundary.
  2. Manual Decomposition: Manually break down the complex shape into multiple simple, non-intersecting polygons, create a separate body for each, and combine them using Matter.Body.create({ parts: [...] }).
  3. Ensure Consistent Winding: Always ensure the final vertex arrays are wound in a consistent direction (clockwise or counter-clockwise) without crossing lines before passing them to Bodies.fromVertices.