← Back to home
Workshop series

Virtual Screen Instead of window.innerWidth Everywhere

Adaptive Layout for a Poki HTML5 Game Section 2 of 11

1. Virtual Screen Instead of window.innerWidth Everywhere

The main screen config is in game/src/config/game.config.ts:

screen: {
  land: { width: 960, height: 540 },
  port: { width: 540, height: 960 },
  dpr: 'auto',
  maxDpr: 2,
  render: {
    scaleFactor: GameRenderScalePolicy.resolveScaleFactor(),
  },
},

I define two base virtual coordinate systems:

  • landscape: 960 x 540
  • portrait: 540 x 960

It does not mean the game is always exactly this size. The engine reads the real viewport, detects orientation, and computes a virtual canvas that fills the available area.

Simplified code from engine/screen/Scaler.ts:

const isLand = viewport.width >= viewport.height;
const orientation = isLand ? 'land' : 'port';
const virt = isLand ? this.config.land : this.config.port;

const useWidth = fit === 'width' || (fit === 'auto' && isLand);
const useHeight = fit === 'height' || (fit === 'auto' && !isLand);

const canvasWidth = useWidth ? virt.width : Math.round(virt.height * (vw / vh));
const canvasHeight = useHeight ? virt.height : Math.round(virt.width * (vh / vw));

The idea is:

  • in landscape, width is usually the stable axis;
  • in portrait, height is usually the stable axis;
  • the other axis is derived from the actual aspect ratio;
  • gameplay code works in virtual coordinates;
  • the canvas visually fills the viewport.

This keeps game layout predictable and avoids spreading browser dimensions across the codebase.