Extract Vertices from PNG for Matter.js Bodies

Extracting vertices from a PNG image allows you to generate accurate, custom physics hitboxes for complex 2D sprites in Matter.js instead of relying on basic geometric primitives. This process involves drawing the PNG onto an offscreen HTML5 canvas, reading its alpha channel to identify the outline using a contour-tracing algorithm, simplifying those points into an ordered coordinate path, and passing the resulting vertex array into Matter.js's Bodies.fromVertices method.

1. Enable Concave Decomposition

Matter.js supports concave polygon bodies through an external library called poly-decomp.js. Because auto-traced image outlines are almost always concave, this dependency must be loaded and registered before creating bodies from vertex sets:

// Provide poly-decomp to Matter.js
window.decomp = require('poly-decomp'); // or include via <script> tag

2. Read Image Data with an Offscreen Canvas

To detect the shape of the sprite, load the image and draw it onto an invisible HTML5 canvas element. This provides raw access to the RGBA values of each pixel via getImageData.

function getPixelData(image) {
  const canvas = document.createElement('canvas');
  canvas.width = image.width;
  canvas.height = image.height;
  const ctx = canvas.getContext('2d');
  ctx.drawImage(image, 0, 0);
  return ctx.getImageData(0, 0, image.width, image.height);
}

3. Trace the Alpha Outline

The boundary of the PNG is defined by the transition between fully transparent pixels (alpha = 0) and opaque pixels (alpha > threshold). To turn this boundary into an ordered sequence of coordinates:

  1. Thresholding: Iterate through the image pixels and create a 2D binary grid where 1 represents an opaque pixel and 0 represents empty space.
  2. Contour Extraction: Run a boundary-following algorithm such as the Marching Squares or Moore-Neighbor tracing algorithm over the binary grid. This produces an ordered loop of { x, y } points defining the perimeter.

Third-party utilities like marching-squares or SVG vectorizers can be used to handle this step efficiently.

4. Reduce and Optimize the Vertex Count

Raw contour tracing often generates hundreds or thousands of individual vertices—one for every edge pixel. Passing too many vertices into a physics engine creates severe performance bottlenecks.

Pass the traced points through a polyline simplification algorithm, such as the Ramer-Douglas-Peucker (RDP) algorithm. This eliminates redundant points along straight edges and curves while preserving the essential silhouette of the object, reducing the vertex count to a manageable number (typically 20 to 50 points).

5. Generate the Matter.js Body

Once you have a simplified array of coordinates in clockwise or counter-clockwise order, pass the array to Matter.Bodies.fromVertices.

// vertices: Array of points [{ x: 10, y: 0 }, { x: 30, y: 20 }, ...]
const vertices = getSimplifiedVertices(image);

const body = Matter.Bodies.fromVertices(spawnX, spawnY, vertices, {
  render: {
    sprite: {
      texture: 'path/to/sprite.png'
    }
  }
}, true);

Matter.Composite.add(engine.world, body);

The fourth parameter is an options object, and the optional boolean parameter (true) enables internal vertex correction and automated hull decomposition. Matter.js automatically recalculates the center of mass, repositions the vertices relative to that center, and splits concave geometries into a compound body composed of convex sub-polygons.