PixiJS Resolution Scaling: Set a DPR‑Aware Cap for Sharp Sprites
Learn how PIXI.settings.RESOLUTION maps logical pixels to device pixels, why a fixed default can blur sprites on retina or waste memory on low‑end phones, and how to cap it per device class.
03 Dec 2025, 14:45 UTC

The problem: soft sprites on retina, heavy on phones
You notice that the same PixiJS code looks crisp on a low‑dpi laptop but appears blurry on a MacBook Pro retina display, while on an Android phone the frame rate drops and memory usage spikes. The root cause is usually the default value of PIXI.settings.RESOLUTION, which stays at 1 for every device.
How PixiJS resolution maps logical to device pixels
The devicePixelRatio (DPR) tells you how many physical pixels correspond to one CSS pixel. A DPR of 2 means one logical pixel occupies four device pixels. PixiJS renders to a WebGL backing store whose size is the logical canvas size multiplied by PIXI.settings.RESOLUTION. When RESOLUTION equals the DPR, each logical pixel maps to a physical pixel, giving a sharp image. If RESOLUTION is lower than the DPR, the renderer upscales the backing store, causing blur; if it is higher, the backing store grows larger than needed, consuming more texture memory and GPU bandwidth.
Choosing a DPR‑aware cap
Instead of a fixed value, set RESOLUTION to the smaller of the device pixel ratio and a maximum you decide for your target hardware. This keeps the backing store close to the physical pixel count on high‑DPI screens while preventing unnecessary upscaling on low‑end devices.
Worked example: cap resolution for a sprite‑heavy UI
Run this in a browser module after PixiJS is loaded. No special permissions are required. Replace the texture URL with your own asset.
import * as PIXI from 'pixi.js';
// Determine a cap: use DPR but never exceed 2 (adjust as needed)
const dpr = window.devicePixelRatio || 1;
const resolutionCap = Math.min(dpr, 2);
PIXI.settings.RESOLUTION = resolutionCap;
const app = new PIXI.Application({
width: 960,
height: 540,
resolution: PIXI.settings.RESOLUTION,
autoDensity: true // makes the CSS size match the logical size
});
document.body.appendChild(app.view);
// Load a sprite whose source texture matches the intended resolution
const texture = await PIXI.Assets.load('https://example.com/ui-icon.png');
const sprite = new PIXI.Sprite(texture);
sprite.anchor.set(0.5);
sprite.x = app.screen.width / 2;
sprite.y = app.screen.height / 2;
app.stage.addChild(sprite);
// Diagnostic output
console.log('RESOLUTION setting', PIXI.settings.RESOLUTION);
console.log('logical screen size', app.screen.width, app.screen.height);
console.log('backing store size', app.renderer.width, app.renderer.height);
Expected checks: app.screen.width remains the logical width you passed (960), while app.renderer.width should equal 960 * PIXI.settings.RESOLUTION. The sprite’s logical dimensions stay unchanged; only the backing store and texture sampling scale.
Trade‑off and limitation
Increasing RESOLUTION above 1 improves visual fidelity on retina displays but raises WebGL draw‑call overhead and GPU texture bandwidth. Memory usage for textures grows proportionally to the multiplier, so a RESOLUTION of 2 doubles the width and height of each texture, quadrupling the memory footprint.
If you force RESOLUTION higher than 1 on a mobile browser whose source textures are low‑resolution, the renderer will upscale those textures, leading to visible pixelation. Keep source assets at least as large as the target resolution, or provide higher‑resolution variants for high‑DPI devices.
Texture auto‑detection behavior changed between PixiJS v5 and v6; after an upgrade verify that your loader still creates textures of the expected size and that batching is not broken.
Practical way to verify the result
- Open a minimal PixiJS canvas in Chrome DevTools.
- Toggle the device toolbar and set the pixel ratio to 2 (or any value you want to test).
- Confirm that the sprite’s logical position (based on
app.screen.width) stays centered while the backing store dimensions reported byapp.renderer.widthandapp.renderer.heightscale with the chosen RESOLUTION. - Alternatively, run the diagnostic logs from the example and compare the numbers.
Actionable closing
Start with RESOLUTION = 1 for broad mobile support, cap at 2 for desktop‑class retina displays, and only go higher when you have source art that justifies it. Measure backing store size and visual crispness per device class, and lower the cap if you observe frame‑rate drops or memory pressure. This approach gives you crisp sprites where they matter without wasting GPU resources elsewhere.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.