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.