scale-and-responsive
Scale and Responsive Design
How to use the ScaleManager for scaling, centering, fullscreen, orientation handling, and responsive resize in Phaser 4.
Key source paths: src/scale/ScaleManager.js, src/scale/const/, src/scale/events/, src/core/typedefs/ScaleConfig.js
Related skills: ../game-setup-and-config/SKILL.md
Quick Start
Scale-to-fit with centering -- the most common setup for responsive games:
const config = {
type: Phaser.AUTO,
scale: {
parent: 'game-container',
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
width: 800,
height: 600
},
scene: MyScene
};
const game = new Phaser.Game(config);
The scale config object is parsed into a Phaser.Core.Config instance which the ScaleManager reads during boot. The ScaleManager sets the canvas element size and applies CSS scaling to fit it within its parent.
Core Concepts
ScaleManager (src/scale/ScaleManager.js)
The ScaleManager is created during the Game boot sequence and is accessible at game.scale or this.scale from within a Scene. It extends EventEmitter.
Three internal Size components drive all calculations:
| Component | Property | Purpose |
|---|---|---|
gameSize |
game.scale.gameSize |
The unmodified game dimensions from config. Used for world bounds, cameras. Read via game.scale.width / game.scale.height. |
baseSize |
game.scale.baseSize |
The auto-rounded gameSize. Sets the actual canvas.width and canvas.height attributes. |
displaySize |
game.scale.displaySize |
The CSS-scaled canvas size after applying scale mode, parent bounds, and zoom. Sets canvas.style.width / canvas.style.height. |
Scaling works by keeping the canvas element dimensions fixed (baseSize) and stretching it via CSS properties (displaySize). This is equivalent to CSS transform-scale but without browser prefix issues.
The displayScale property (Phaser.Math.Vector2) holds the ratio baseSize / canvasBounds and is used internally for input coordinate transformation.
Scale Modes (src/scale/const/SCALE_MODE_CONST.js)
All modes are on Phaser.Scale.ScaleModes and are set via scale.mode in config:
| Constant | Value | Behavior |
|---|---|---|
NONE |
0 | No automatic scaling. Canvas uses config width/height. You manage sizing yourself. If you resize the canvas externally, call game.scale.resize(w, h) to update internals. |
WIDTH_CONTROLS_HEIGHT |
1 | Height adjusts automatically to maintain aspect ratio based on width. |
HEIGHT_CONTROLS_WIDTH |
2 | Width adjusts automatically to maintain aspect ratio based on height. |
FIT |
3 | Scales to fit inside parent while preserving aspect ratio. May leave empty space (letterbox/pillarbox). Most commonly used mode. |
ENVELOP |
4 | Scales to cover the entire parent while preserving aspect ratio. May extend beyond parent bounds (content gets cropped). |
RESIZE |
5 | Canvas element itself is resized to fill parent. No CSS scaling -- 1:1 pixel mapping. The gameSize, baseSize, and displaySize all change to match parent. Beware of GPU fill-rate on large displays. |
EXPAND |
6 | Hybrid of RESIZE and FIT. The visible area resizes to fill the parent, and the canvas scales to fit inside that area. Added in v3.80. |
Shorthand constants are also available directly on Phaser.Scale: Phaser.Scale.FIT, Phaser.Scale.RESIZE, etc.
Center Modes (src/scale/const/CENTER_CONST.js)
Set via scale.autoCenter in config. Centering is achieved by setting CSS marginLeft and marginTop on the canvas:
| Constant | Value | Behavior |
|---|---|---|
NO_CENTER |
0 | No auto-centering (default). |
CENTER_BOTH |
1 | Center horizontally and vertically within parent. |
CENTER_HORIZONTALLY |
2 | Center horizontally only. |
CENTER_VERTICALLY |
3 | Center vertically only. |
The parent element must have calculable bounds. If the parent has no defined width/height, centering will not work correctly.
Zoom (src/scale/const/ZOOM_CONST.js)
Set via scale.zoom in config. Multiplies the display size:
| Constant | Value | Behavior |
|---|---|---|
NO_ZOOM |
1 | No zoom (default). |
ZOOM_2X |
2 | 2x zoom -- good for pixel art at low base resolution. |
ZOOM_4X |
4 | 4x zoom. |
MAX_ZOOM |
-1 | Automatically calculates the largest integer zoom that fits in the parent. |
You can also pass any numeric value. The zoom affects CSS display size but not the canvas resolution.
Orientation (src/scale/const/ORIENTATION_CONST.js)
Read via game.scale.orientation. Values are strings:
Phaser.Scale.Orientation.LANDSCAPE='landscape-primary'Phaser.Scale.Orientation.LANDSCAPE_SECONDARY='landscape-secondary'Phaser.Scale.Orientation.PORTRAIT='portrait-primary'Phaser.Scale.Orientation.PORTRAIT_SECONDARY='portrait-secondary'
Convenience booleans: game.scale.isPortrait, game.scale.isLandscape (device orientation), game.scale.isGamePortrait, game.scale.isGameLandscape (game dimensions).
Common Patterns
FIT Mode with Centering (Most Common)
scale: {
parent: 'game-container',
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
width: 1280,
height: 720
}
The game maintains its 16:9 aspect ratio and centers within the parent. Empty space appears as letterbox/pillarbox bars. Style the parent's background color to control bar appearance.
Responsive Resize (Dynamic Canvas Size)
scale: {
parent: 'game-container',
mode: Phaser.Scale.RESIZE,
width: '100%',
height: '100%'
}
// In your scene -- respond to size changes:
create() {
this.scale.on('resize', this.handleResize, this);
}
handleResize(gameSize, baseSize, displaySize) {
this.cameras.resize(gameSize.width, gameSize.height);
// Reposition UI elements based on new dimensions
}
Width/height accept percentage strings (e.g., '100%') which resolve against the parent size. If the parent has no size, they fall back to window.innerWidth / window.innerHeight.
Fullscreen Toggle
// Must be called from a pointerup gesture (not pointerdown)
this.input.on('pointerup', () => {
if (this.scale.isFullscreen) {
this.scale.stopFullscreen();
} else {
this.scale.startFullscreen();
}
});
// Or use the convenience method:
this.input.on('pointerup', () => {
this.scale.toggleFullscreen();
});
startFullscreen(fullscreenOptions)-- requests browser fullscreen. Default options:{ navigationUI: 'hide' }.stopFullscreen()-- exits fullscreen mode.toggleFullscreen(fullscreenOptions)-- toggles between the two.isFullscreen-- read-only boolean for current state.
If no fullscreenTarget is configured, Phaser creates a temporary <div>, moves the canvas into it, and sends that div fullscreen. The div is removed when leaving fullscreen.
For iframes, the allowfullscreen attribute is required.
Mobile Orientation Handling
create() {
this.scale.on('orientationchange', (orientation) => {
if (orientation === Phaser.Scale.LANDSCAPE) {
// Show game UI
} else {
// Show "rotate device" message
}
});
// Lock orientation (mobile browsers only, limited support):
this.scale.lockOrientation('landscape');
}
Fixed Size (No Scaling)
scale: {
mode: Phaser.Scale.NONE,
width: 800,
height: 600
}
In NONE mode, if you change the canvas size externally you must call game.scale.resize(newWidth, newHeight) to update all internal components including input coordinates.
Pixel Art with Max Zoom
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
width: 320,
height: 240,
zoom: Phaser.Scale.MAX_ZOOM
},
pixelArt: true
MAX_ZOOM calculates the largest integer multiplier that fits in the parent. Combined with pixelArt: true (which disables anti-aliasing), this gives crisp pixel rendering.
Snap Values for Grid Alignment
scale: {
mode: Phaser.Scale.FIT,
width: 800,
height: 600,
snap: { width: 16, height: 16 }
}
When the browser resizes, dimensions snap down to the nearest grid multiple (using floor). Best used with FIT mode. Can also be set at runtime: game.scale.setSnap(16, 16) or reset with game.scale.setSnap().
Min/Max Size Constraints
scale: {
mode: Phaser.Scale.FIT,
width: 800,
height: 600,
min: { width: 400, height: 300 },
max: { width: 1600, height: 1200 }
}
The display size is clamped to these bounds during scaling calculations.
Configuration Reference
ScaleConfig (src/core/typedefs/ScaleConfig.js)
All properties go inside the scale object in your game config:
| Property | Type | Default | Description |
|---|---|---|---|
width |
number|string |
1024 |
Base game width. Strings like '100%' resolve against parent size. |
height |
number|string |
768 |
Base game height. Strings like '100%' resolve against parent size. |
zoom |
number|ZoomType |
1 |
Canvas zoom factor. Use Phaser.Scale.MAX_ZOOM for auto. |
parent |
HTMLElement|string|null |
undefined |
Parent DOM element or its ID. undefined = document.body. null = no parent (you manage canvas placement). |
expandParent |
boolean |
true |
Allow ScaleManager to set parent/body CSS height to 100%. |
mode |
ScaleModeType |
NONE |
Scale mode constant (see table above). |
min |
{width, height} |
- | Minimum canvas dimensions. |
max |
{width, height} |
- | Maximum canvas dimensions. |
snap |
{width, height} |
- | Snap values for resize rounding. |
autoRound |
boolean |
false |
Floor display/style sizes for low-powered device performance. |
autoCenter |
CenterType |
NO_CENTER |
Auto-centering mode (see table above). |
resizeInterval |
number |
500 |
Milliseconds between parent size checks (fallback polling). |
fullscreenTarget |
HTMLElement|string |
undefined |
Element to send fullscreen. If not set, Phaser creates a wrapper div. |
Events (src/scale/events/)
All events are on Phaser.Scale.Events and are emitted by the ScaleManager instance (game.scale):
| Event | String Value | Callback Signature | When |
|---|---|---|---|
RESIZE |
'resize' |
(gameSize, baseSize, displaySize, previousWidth, previousHeight) |
Any resize, refresh, or scale change. The three Size parameters have .width, .height, .aspectRatio. |
ORIENTATION_CHANGE |
'orientationchange' |
(orientation) |
Device orientation changes. orientation is a string constant from Phaser.Scale.Orientation. |
ENTER_FULLSCREEN |
'enterfullscreen' |
() |
Browser successfully enters fullscreen. |
LEAVE_FULLSCREEN |
'leavefullscreen' |
() |
Browser leaves fullscreen (via code or user ESC). |
FULLSCREEN_FAILED |
'fullscreenfailed' |
(error) |
Fullscreen request was denied by the browser. |
FULLSCREEN_UNSUPPORTED |
'fullscreenunsupported' |
() |
Browser does not support the Fullscreen API. |
API Quick Reference
Key Properties (read-only unless noted)
| Property | Type | Description |
|---|---|---|
width |
number |
Game width (from gameSize.width). |
height |
number |
Game height (from gameSize.height). |
isFullscreen |
boolean |
Currently in fullscreen mode. |
isPortrait |
boolean |
Device is in portrait orientation. |
isLandscape |
boolean |
Device is in landscape orientation. |
isGamePortrait |
boolean |
Game dimensions are taller than wide. |
isGameLandscape |
boolean |
Game dimensions are wider than tall. |
scaleMode |
number |
Current scale mode constant. |
orientation |
string |
Current orientation string. |
zoom |
number |
Current zoom factor. |
autoRound |
boolean |
Whether sizes are auto-floored. |
autoCenter |
number |
Current center mode constant. |
parentSize |
Size |
Computed parent dimensions. |
gameSize |
Size |
Unmodified game dimensions. |
baseSize |
Size |
Canvas element dimensions. |
displaySize |
Size |
CSS-scaled display dimensions. |
displayScale |
Vector2 |
Ratio of baseSize to canvasBounds (used for input mapping). |
canvas |
HTMLCanvasElement |
The game canvas element. |
canvasBounds |
Rectangle |
DOM bounding rect of the canvas. |
parent |
HTMLElement |
The parent DOM element. |
parentIsWindow |
boolean |
True if parent is document.body. |
resizeInterval |
number |
Polling interval in ms (writable). |
Key Methods
| Method | Returns | Description |
|---|---|---|
resize(width, height) |
this |
For NONE mode: sets game, base, and display sizes directly. Updates canvas element and CSS. |
setGameSize(width, height) |
this |
For scaled modes (FIT, etc.): changes the base game size and re-applies scaling. |
setParentSize(width, height) |
this |
Manually set parent dimensions (useful when parent: null). |
setZoom(value) |
this |
Change zoom factor at runtime. |
setMaxZoom() |
this |
Set zoom to maximum integer that fits parent. |
setSnap(snapWidth, snapHeight) |
this |
Set or reset snap grid values. |
getMaxZoom() |
number |
Calculate (but don't apply) the max zoom. |
getViewPort(camera?, out?) |
Rectangle |
Get the visible area rectangle, optionally for a specific camera. |
refresh(prevW?, prevH?) |
this |
Force recalculation of scale, bounds, orientation. Emits RESIZE. |
startFullscreen(options?) |
void |
Enter fullscreen. Must be called from pointerup. |
stopFullscreen() |
void |
Exit fullscreen. |
toggleFullscreen(options?) |
void |
Toggle fullscreen state. |
lockOrientation(orientation) |
boolean |
Attempt to lock screen orientation (mobile only). |
transformX(pageX) |
number |
Convert DOM pageX to game coordinate. |
transformY(pageY) |
number |
Convert DOM pageY to game coordinate. |
getParentBounds() |
boolean |
Recalculate parent size. Returns true if changed. |
updateCenter() |
void |
Recalculate and apply centering margins. |
updateBounds() |
void |
Update canvasBounds from the canvas bounding client rect. |
Gotchas
-
Parent element must have dimensions. The ScaleManager relies on the parent's computed CSS size. An unstyled
<div>has zero height, which breaks centering and scaling. Either give it explicit CSS dimensions or letexpandParent: true(the default) set body/parent to 100% height. -
No padding on the parent. The ScaleManager does not account for parent padding. Apply padding to a wrapper element instead, or use margins on the parent's parent.
-
Do not style the canvas directly. The ScaleManager controls
canvas.style.width,canvas.style.height,marginLeft, andmarginTop. External CSS on the canvas will conflict. -
Fullscreen requires
pointerup, notpointerdown. On touch devices,pointerdownfullscreen requests are blocked unless the document already received touch input. Always usepointerupfor reliable behavior. -
Fullscreen in iframes needs
allowfullscreen. Without this attribute on the iframe element, fullscreen requests will silently fail. -
RESIZE mode and GPU fill-rate. RESIZE creates a 1:1 pixel canvas matching the parent. On high-resolution displays this can be very large. Monitor performance on low-end devices.
-
resize()vssetGameSize(). Useresize()only in NONE mode (it sets everything directly). UsesetGameSize()when using FIT/ENVELOP/etc. (it preserves the scale mode calculations). -
iOS height quirks. When the parent is the window on iOS, the ScaleManager uses
GetInnerHeightto work around Safari's dynamic toolbar affectinggetBoundingClientRect. -
Percentage strings require a sized parent. Setting
width: '100%'orheight: '100%'resolves againstparentSize. If the parent has no size, it falls back towindow.innerWidth/innerHeight. -
resizeIntervalis a fallback poll. Modern browsers dispatchresizeevents, but the ScaleManager also polls everyresizeIntervalms (default 500) to catch edge cases on older browsers.
Source File Map
| File | Purpose |
|---|---|
src/scale/ScaleManager.js |
Main class -- all scaling, centering, fullscreen, orientation logic. Access via game.scale. |
src/scale/const/SCALE_MODE_CONST.js |
Scale mode constants: NONE, WIDTH_CONTROLS_HEIGHT, HEIGHT_CONTROLS_WIDTH, FIT, ENVELOP, RESIZE, EXPAND. |
src/scale/const/CENTER_CONST.js |
Center mode constants: NO_CENTER, CENTER_BOTH, CENTER_HORIZONTALLY, CENTER_VERTICALLY. |
src/scale/const/ZOOM_CONST.js |
Zoom constants: NO_ZOOM, ZOOM_2X, ZOOM_4X, MAX_ZOOM. |
src/scale/const/ORIENTATION_CONST.js |
Orientation string constants: LANDSCAPE, LANDSCAPE_SECONDARY, PORTRAIT, PORTRAIT_SECONDARY. |
src/scale/events/RESIZE_EVENT.js |
Resize event definition. Callback receives gameSize, baseSize, displaySize, previousWidth, previousHeight. |
src/scale/events/ENTER_FULLSCREEN_EVENT.js |
Emitted on successful fullscreen entry. |
src/scale/events/LEAVE_FULLSCREEN_EVENT.js |
Emitted when leaving fullscreen. |
src/scale/events/FULLSCREEN_FAILED_EVENT.js |
Emitted when fullscreen request is denied. |
src/scale/events/FULLSCREEN_UNSUPPORTED_EVENT.js |
Emitted when browser lacks Fullscreen API support. |
src/scale/events/ORIENTATION_CHANGE_EVENT.js |
Emitted on device orientation change. Callback receives orientation string. |
src/core/typedefs/ScaleConfig.js |
JSDoc typedef for the scale config object. |
src/structs/Size.js |
The Size component class used by gameSize, baseSize, displaySize. |
src/dom/GetInnerHeight.js |
iOS-specific workaround for getting accurate viewport height. |
src/dom/GetScreenOrientation.js |
Determines current screen orientation from dimensions. |