Adjust Pixel Ratio for High DPI in Matter.js

High-DPI and Retina displays often render standard HTML5 canvases with blurry or pixelated visuals because the physical screen pixels outnumber the logical CSS pixels. In Matter.js, you can easily fix this issue by configuring the pixelRatio property within the renderer settings. This article explains how to configure and adjust the pixel ratio during initialization and dynamically update it to ensure crisp, sharp rendering across all displays.

Setting Pixel Ratio on Initialization

When creating a renderer using Matter.Render.create(), pass the pixelRatio option inside the options object. You can either set it to 'auto' to let Matter.js detect the screen density automatically, or explicitly pass window.devicePixelRatio.

const { Engine, Render, Runner, Bodies, Composite } = Matter;

const engine = Engine.create();

// Create the renderer with high DPI support
const render = Render.create({
    element: document.body,
    engine: engine,
    options: {
        width: 800,
        height: 600,
        wireframes: false,
        pixelRatio: window.devicePixelRatio || 1 // or simply 'auto'
    }
});

Render.run(render);
Runner.run(Runner.create(), engine);

How Matter.js Handles Pixel Ratio

When pixelRatio is greater than 1, Matter.js automatically scales the underlying HTML5 <canvas> element:

  1. Drawing Buffer: It multiplies the internal canvas resolution (canvas.width and canvas.height) by the specified pixel ratio to match physical screen pixels.
  2. Display Size: It sets the CSS styling (canvas.style.width and canvas.style.height) to your target dimensions (e.g., 800x600 px).
  3. Context Scale: It scales the 2D drawing context so that coordinates in your physics world continue to map 1:1 with logical screen coordinates, preserving the positions and sizes of your bodies without requiring manual transformation.

Updating Pixel Ratio Dynamically

If users drag the browser window between displays with different pixel densities (such as moving from an external 1080p monitor to a built-in Retina display), you can update the ratio on the fly using Matter.Render.setPixelRatio():

function updatePixelRatio(render) {
    const newRatio = window.devicePixelRatio || 1;
    Render.setPixelRatio(render, newRatio);
}

// Listen for resolution changes
window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`)
    .addEventListener('change', () => {
        updatePixelRatio(render);
    });

Using Matter.Render.setPixelRatio() ensures that the canvas buffer size, styles, and context transforms are updated immediately without needing to recreate the entire physics engine or renderer instance.