How to Sync PixiJS Sprites with Matter.js

Synchronizing PixiJS sprites with Matter.js rigid bodies allows you to leverage Matter.js for accurate 2D physics simulation while using PixiJS for high-performance WebGL rendering. Because Matter.js calculates motion and collision data headlessly without drawing graphics, you must manually bind each visual sprite's position and rotation properties to its corresponding physics body inside an active rendering loop.

1. Align the Sprite Anchor Point

By default, PixiJS positions and rotates sprites relative to their top-left corner (0, 0). In contrast, Matter.js defines a body's position and rotation around its center of mass. To ensure the sprite visually aligns with the physics body during movement and rotation, set the sprite's anchor to its center:

sprite.anchor.set(0.5);

2. Associate the Sprite with the Body

To update pairs efficiently, establish a direct reference between each Matter.js body and its corresponding PixiJS sprite. The simplest method is attaching the sprite directly to the body object:

const body = Matter.Bodies.rectangle(x, y, width, height);
const sprite = PIXI.Sprite.from('texture.png');
sprite.anchor.set(0.5);

// Link the sprite to the body
body.plugin = { sprite: sprite };

Matter.Composite.add(engine.world, body);
app.stage.addChild(sprite);

Alternatively, you can maintain an array of paired references:

const physicsPairs = [];
physicsPairs.push({ body, sprite });

3. Update Position and Rotation in the Render Loop

Inside the PixiJS ticker or your main requestAnimationFrame loop, update the physics engine first, then transfer the calculated coordinates and rotation angles from the Matter.js body to the PixiJS sprite.

app.ticker.add(() => {
    // 1. Advance the physics engine
    Matter.Engine.update(engine, app.ticker.deltaMS);

    // 2. Sync properties for each paired object
    for (const pair of physicsPairs) {
        pair.sprite.position.x = pair.body.position.x;
        pair.sprite.position.y = pair.body.position.y;
        pair.sprite.rotation = pair.body.angle;
    }
});

Complete Implementation Example

import * as PIXI from 'pixi.js';
import Matter from 'matter-js';

// Setup PixiJS Application
const app = new PIXI.Application({ width: 800, height: 600 });
document.body.appendChild(app.view);

// Setup Matter.js Engine
const engine = Matter.Engine.create();
const world = engine.world;

// Create a Matter.js Box Body
const boxBody = Matter.Bodies.rectangle(400, 100, 60, 60, { restitution: 0.8 });
const groundBody = Matter.Bodies.rectangle(400, 580, 810, 40, { isStatic: true });
Matter.Composite.add(world, [boxBody, groundBody]);

// Create PixiJS Sprites
const boxSprite = PIXI.Sprite.from(PIXI.Texture.WHITE);
boxSprite.width = 60;
boxSprite.height = 60;
boxSprite.tint = 0xff0000;
boxSprite.anchor.set(0.5);
app.stage.addChild(boxSprite);

// Animation Loop
app.ticker.add(() => {
    Matter.Engine.update(engine, app.ticker.deltaMS);

    // Synchronize coordinates and rotation
    boxSprite.position.set(boxBody.position.x, boxBody.position.y);
    boxSprite.rotation = boxBody.angle;
});