How to implement letterbox scaling in PixiJS

Nassim

When making a game you usually want to preserve an aspect ratio regardless of the size of the player's screen. This is called letterbox scaling.

Here is an example. Down below is an implementation in PixiJS.

letterbox scaling example

Here is the CSS you need :

html, body {
  background: black;
}

#pixi-canvas {
  position: absolute;
  top: 0; left: 0; right: 0; bottom: 0;
  margin: auto;
}

Now here is the JS code :

import { Application, Sprite, SCALE_MODES} from 'pixi.js'

const logicalWidth = 160
const logicalHeight = 144

const app = new Application({
  width: logicalWidth,
  height: logicalHeight,
  resolution: window.devicePixelRatio || 1,
  background: '#1099bb'
})

app.view.id = 'pixi-canvas'

document.body.appendChild(app.view)

const bunny = Sprite.from('https://s3-us-west-2.amazonaws.com/s.cdpn.io/169700/bunny.png')

bunny.x = logicalWidth / 2
bunny.y = logicalHeight / 2

bunny.anchor.x = 0.5
bunny.anchor.y = 0.5

bunny.texture.baseTexture.scaleMode = SCALE_MODES.NEAREST

function resizeHandler() {
  const scaleFactor = Math.min(
    window.innerWidth / logicalWidth,
    window.innerHeight / logicalHeight
  )

  const newWidth = Math.ceil(logicalWidth * scaleFactor)
  const newHeight = Math.ceil(logicalHeight * scaleFactor)

  app.view.style.width = `${newWidth}px`
  app.view.style.height = `${newHeight}px`

  app.scale.set(scaleFactor)
}


app.stage.addChild(logo)

app.ticker.add(() => {
  bunny.rotation += 0.01
})
window.addEventListener('resize', resizeHandler, false)
resizeHandler()

Observations

logicalWidth and logicalHeight are the width and height we original design for. Here I've used the original width and height of the Game Boy.

resizeHandler contains the math needed to pull off letterbox scaling.

A bit of a tagent but you'll notice the use of SCALE_MODES.NEAREST on the bunny's texture. This is what you need to use when displaying pixelart so it won't become blurry.

That's pretty much it for the observations. Hope this is useful.