Mastodon Icon RSS Icon GitHub Icon LinkedIn Icon RSS Icon

Quick Dev Tips: Pixel-Perfect Scaling of a Phaser game

As you may know, I am in the middle of developing a demo for some Smart Pathfinding, and I’m using Phaser and TypeScript for it. Unfortunately, as soon as I started coding a couple of days ago, I quickly found a problem. How can I do pixel-perfect scaling of the game?

The tileset I’m using is very small (16x16 pixels tiles) and I needed to scale them at least 3 times to make them visible on a big screen. However, searching for “phaser.io” and “scaling” returns a lot of not useful results.

Results are often about the wrong thing (for instance, scaling a single sprite or a tilemap) or old (that is, something that is not true on Phaser 2.5.0). But a solution exists! And I’m here for this!

1
2
3
4
this.game.scale.scaleMode = Phaser.ScaleManager.USER_SCALE;
this.game.scale.setUserScale(3, 3);
this.game.renderer.renderSession.roundPixels = true;
Phaser.Canvas.setImageRenderingCrisp(this.game.canvas);

Explanation

This 4 lines of code solved my problems. I put them in the Phaser’s preload function as a first thing. Let’s see how this work line-by-line:

  1. Line 1. We set the scaleMode variable of our Phaser.Game instance to Phaser.ScaleManager.USE_SCALE. This means that the ScaleManager subsystem (the one that is in charge of scaling the game) will accept a manual scale specification.
  2. Line 2. And this is the manual scale specification. We say that we want to scale the full windows by 3 in all directions.
  3. Line 3. This is a bit mysterious at first. This line says to the renderer system that we want to round the sprite position to the integer value. This is to avoid to render sprite pixels in sub-pixels positions. To better understand, consider that by scaling by 3 the game, each pixel in our images corresponds to a square of 3x3 pixels in the game windows. This will ensure that moving the image by 1 pixel in the “not scaled world” correspond to a 3px movement in the actual game windows.
  4. Line 4. This is the last step. We want a crisp rendering. In a pixel perfect game, we want to see our beautiful square pixels. So we say to the rendering system that we don’t want antialiasing or other things like that.
The difference between anti-aliased (left) and crisp (right) scaling.
Figure 1. The difference between anti-aliased (left) and crisp (right) scaling.

Conclusion

This guide is updated to Phaser 2.5.0. I hope you can find this useful and do not waste a full 30 minutes on this trivial task! :)