How to Smoothly Center Matter.js Camera

Centering the camera smoothly on a moving player in Matter.js involves tracking the player's physical coordinates and applying linear interpolation (lerp) to the render bounds on each frame. By decoupling the camera's current position from the player’s immediate coordinates and transitioning between them incrementally, you eliminate rigid snapping and create a fluid tracking effect. This guide explains how to configure your renderer for custom bounds, implement a lerp function, and update the viewport within the engine's update loop.

Enable Viewport Bounds in Matter.js

By default, the Matter.js Render module displays a fixed view of the physics world. To enable dynamic camera manipulation, you must configure the renderer to support bounds when creating it.

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

Setting hasBounds: true allows the rendering viewport to be scaled and translated relative to the world coordinates.

Implement Linear Interpolation (Lerp)

Linear interpolation calculates an intermediate value between a starting point and a target point based on a smoothing factor (alpha). The formula smoothly closes the distance between the camera and the player:

function lerp(start, end, alpha) {
    return start + (end - start) * alpha;
}

An alpha value between 0.05 and 0.1 provides a natural, smooth trailing camera effect. Lower values result in a slower, looser follow, while values closer to 1.0 create tighter tracking.

Update the Camera Position

To smoothly follow the player, store the camera’s current center coordinates as a separate object. On every engine update, lerp the camera position toward the player’s physical position, then adjust the renderer’s bounds.

// Initial camera center
const camera = {
    x: player.position.x,
    y: player.position.y
};

const viewWidth = render.options.width;
const viewHeight = render.options.height;
const smoothingFactor = 0.08;

// Hook into the engine's afterUpdate event
Matter.Events.on(engine, 'afterUpdate', () => {
    // Smoothly calculate new camera coordinates
    camera.x = lerp(camera.x, player.position.x, smoothingFactor);
    camera.y = lerp(camera.y, player.position.y, smoothingFactor);

    // Apply the centered bounds to the renderer
    render.bounds.min.x = camera.x - viewWidth / 2;
    render.bounds.max.x = camera.x + viewWidth / 2;
    render.bounds.min.y = camera.y - viewHeight / 2;
    render.bounds.max.y = camera.y + viewHeight / 2;
});

Alternative: Using Render.lookAt

Matter.js provides a built-in Matter.Render.lookAt utility that can center viewports. To use it with smoothing, pass a mock bounds object containing your interpolated camera coordinates:

Matter.Events.on(engine, 'afterUpdate', () => {
    camera.x = lerp(camera.x, player.position.x, smoothingFactor);
    camera.y = lerp(camera.y, player.position.y, smoothingFactor);

    Matter.Render.lookAt(render, {
        min: { x: camera.x - viewWidth / 2, y: camera.y - viewHeight / 2 },
        max: { x: camera.x + viewWidth / 2, y: camera.y + viewHeight / 2 }
    });
});

Directly modifying render.bounds or passing the computed bounding box into Render.lookAt ensures that viewport dimensions remain stable while the camera tracks the player across large maps smoothly.