Use this skill when migrating a Phaser 3 project to Phaser 4, or when a user asks about breaking changes, API differences, or how to update their v3 code. Covers renderer changes (pipelines to render nodes), FX/masks to filters, tint system, camera matrix, texture coordinates, DynamicTexture, shaders, lighting, removed game objects, and a full migration checklist. Triggers on: migrate, upgrade, v3 to v4, breaking changes, Phaser 3 to 4, migration guide, update from v3.
This guide covers everything you need to change when upgrading a Phaser v3 project to Phaser v4. It is organized from highest-impact changes to smaller details, so you can work through it top-to-bottom.
Related skills: ../v4-new-features/SKILL.md, ../game-setup-and-config/SKILL.md, ../filters-and-postfx/SKILL.md
Phaser v4 contains a brand-new WebGL renderer. The entire rendering pipeline from v3 has been replaced. This is the single biggest change in v4.
What was removed:
The v3 Pipeline system has been removed entirely. Pipelines frequently held multiple responsibilities (e.g. the Utility pipeline handled various different rendering tasks) and each had to manage WebGL state independently, leading to conflicts where one pipeline could break another's assumptions.
What replaced it:
The new RenderNode architecture. Each render node handles a single rendering task, making the system more maintainable. All render nodes have a run method, and some have a batch method to assemble state from several sources before invoking run.
What this means for you:
RenderConfig#renderNodes to register custom render nodes at boot.WebGLRenderer internals directly, be aware that many internal properties have been removed or restructured:
WebGLRenderer.textureIndexes is removed. Use glTextureUnits.unitIndices instead.WebGLRenderer#genericVertexBuffer and #genericVertexData are removed (freeing ~16MB RAM/VRAM). BatchHandler render nodes now create their own WebGL data buffers.WebGLAttribLocationWrapper is removed.gl calls in a Phaser v4 game. This can change the WebGL state without updating the internal WebGLGlobalWrapper, causing unpredictable behavior. If you need direct WebGL access, use an Extern game object, which resets state after it finishes.The Canvas renderer is still available but should be considered deprecated. Canvas rendering does not support any of the WebGL techniques used in v4's advanced rendering features. As WebGL support is effectively baseline today, we recommend WebGL for all new projects.
Canvas retains one advantage: 27 blend modes vs WebGL's 4 native modes (NORMAL, ADD, MULTIPLY, SCREEN). In v4, the new Blend filter can recreate all Canvas blend modes in WebGL, though it requires indirection through a CaptureFrame, DynamicTexture, or similar.
This is one of the most impactful changes for v3 users who relied on the FX or Mask systems.
What changed:
FX (pre and post) and Masks have been unified into a single Filter system. A Filter takes an input image and produces an output image, usually via a single shader. All filters are mutually compatible.
Key differences from v3:
Extern objects.BitmapMask removed. Use the new Mask filter instead. GeometryMask remains available in Canvas only.Removed derived FX and their replacements:
| v3 FX | v4 Replacement |
|---|---|
Bloom | Phaser.Actions.AddEffectBloom() |
Shine | Phaser.Actions.AddEffectShine() |
Circle | Phaser.Actions.AddMaskShape() |
Gradient | Gradient game object |
ColorMatrix change:
The ColorMatrix filter shifted its color management methods onto a colorMatrix property:
// v3
colorMatrix.sepia();
// v4
colorMatrix.colorMatrix.sepia();
Mask migration:
// v3 - BitmapMask
const mask = new Phaser.Display.Masks.BitmapMask(scene, maskObject);
sprite.setMask(mask);
// v4 - Mask filter
sprite.filters.internal.addMask(maskObject);
The tint system has been overhauled with a new API and additional blend modes.
Removed:
tintFill propertysetTintFill() methodReplacement:
tintMode property or setTintMode() method to control tint blending.Phaser.TintModes enumerates the available modes: MULTIPLY, FILL, ADD, SCREEN, OVERLAY, HARD_LIGHT.How to convert your code:
// v3
sprite.setTintFill(0xff0000);
// v4
sprite.setTint(0xff0000).setTintMode(Phaser.TintModes.FILL);
Other tint changes:
tint and setTint() now purely affect color settings. In v3, calling these would silently deactivate fill mode.The camera matrix system has been rewritten. If you only use standard camera properties (scrollX, scrollY, zoom, rotation), your code should work without changes. However, if you access camera matrices directly, you must update your code.
What changed:
| v3 | v4 |
|---|---|
Camera#matrix = position + rotation + zoom | Camera#matrix = rotation + zoom + scroll (no position) |
| Scroll appended separately | Scroll is part of Camera#matrix |
| No equivalent | Camera#matrixExternal = position only |
| No equivalent | Camera#matrixCombined = matrix * matrixExternal |
If you manipulated scroll factors manually:
// v3
spriteMatrix.e -= camera.scrollX * src.scrollFactorX;
// v4
TransformMatrix.copyWithScrollFactorFrom(matrix, scrollX, scrollY, scrollFactorX, scrollFactorY);
Other camera changes:
GetCalcMatrix() now takes an additional ignoreCameraPosition parameter.GetCalcMatrixResults now includes a matrixExternal property.Phaser v3 used top-left orientation for textures, which caused mismatches internally (framebuffers drawn upside-down, then flipped). Phaser v4 uses GL orientation throughout, where Y=0 is at the bottom.
Action required:
In v3, DynamicTexture allowed you to define batches and perform intricate drawing operations directly. In v4, many of these complex methods have been removed in favor of using the standard rendering system, which handles batching automatically.
Breaking change: DynamicTexture and RenderTexture must now call render() to execute all buffered drawing commands. Previously, draw commands were executed immediately.
New capabilities:
DynamicTexture#preserve() keeps the command buffer for reuse after rendering, allowing you to re-render commands that draw changing game objects.DynamicTexture#callback() inserts a callback to run during command buffer execution.DynamicTexture#capture renders game objects more accurately than draw, capturing the current camera view.RenderTexture.renderMode property: "render" (draw like an Image), "redraw" (update texture during render loop without drawing), or "all" (both). The "redraw" mode enables updating textures mid-render-loop for same-frame shader outputs.TextureManager#addDynamicTexture now has a forceEven parameter.The Shader game object has been significantly rewritten. Existing v3 shaders will need to be updated.
What changed:
ShaderQuadConfig) instead of individual parameters.Shader#setUniform(name, value) method for setting program uniforms individually.Shader#renderImmediate method for rendering outside the regular render loop.The way Phaser loads GLSL code has changed:
#pragma preprocessor directives, which are valid GLSL and compatible with automated syntax checkers. The pragmas are removed before compilation.Lighting has been simplified and enhanced.
How to convert your code:
// v3 - Pipeline-based lighting
sprite.setPipeline('Light2D');
// v4 - Simple method call
sprite.setLighting(true);
Other lighting changes:
z value to set height explicitly, replacing the implicit height based on game resolution from v3.ImageLight filter for image-based lighting, but it is separate from the core lighting system.TileSprite has been internally rewritten to use a new shader that manually controls texture coordinate wrapping instead of relying on WebGL texture wrapping parameters.
What changed:
TileSprite no longer supports texture cropping.New capabilities:
tileRotation property allows rotating the repeating texture.Graphics has a new pathDetailThreshold property (also available as a game config option) that skips vertices within a certain distance of one another, improving performance on complex curves in small areas.Grid shape has renamed properties: it now uses stroke instead of outline, matching the conventions of other Shape objects. Grid also has new controls for rendering gutters between cells and whether to draw outlines on the outside of the grid or just between cells.Rectangle now supports rounded corners.The Geom.Point class and all related functions have been removed. Use Vector2 instead.
Quick reference for method replacements:
v3 (Point) | v4 (Vector2 / Math) |
|---|---|
Point.Ceil | Vector2.ceil |
Point.Floor | Vector2.floor |
Point.Clone | Vector2.clone |
Point.CopyFrom(src, dest) | dest.copy(src) |
Point.Equals | Vector2.equals |
Point.GetCentroid | Math.GetCentroid |
Point.GetMagnitude | Vector2.length |
Point.GetMagnitudeSq | Vector2.lengthSq |
Point.Invert | Vector2.invert |
Point.Negative | Vector2.negate |
Point.SetMagnitude | Vector2.setLength |
Point.Project | Vector2.project |
Point.ProjectUnit | Vector2.projectUnit |
Point.Interpolate | Math.LinearXY |
Point.GetRectangleFromPoints | Math.GetVec2Bounds |
All geometry classes now return Vector2 instead of Point:
The following classes and their static helper functions (getPoint, getPoints, getRandomPoint, CircumferencePoint, Random, etc.) all return Vector2 instances:
Geom.CircleGeom.EllipseGeom.LineGeom.PolygonGeom.RectangleGeom.TriangleIf you have code that checks instanceof Phaser.Geom.Point, update it to check for Phaser.Math.Vector2.
| v3 | v4 | Notes |
|---|---|---|
Math.TAU (was PI / 2) | Math.TAU (now PI * 2) | Value changed! This is now the correct mathematical tau. |
Math.PI2 | Removed | Use Math.TAU instead. |
| No equivalent | Math.PI_OVER_2 | New constant for PI / 2 (what v3's TAU incorrectly was). |
Action required: If you used Math.TAU in v3 expecting PI / 2, replace it with Math.PI_OVER_2. If you used Math.PI2, replace it with Math.TAU.
Phaser.Struct.Set has been replaced with a native JavaScript Set. Methods like iterateLocal are gone. Use standard Set methods (forEach, has, add, delete, etc.).
Phaser.Struct.Map has been replaced with a native JavaScript Map. Methods like contains and setAll are gone. Use standard Map methods (has, get, set, delete, etc.).
The roundPixels game config option now defaults to false (it was true in v3). The behavior has also been refined:
roundPixels only operates when objects are axis-aligned and unscaled, preventing flicker on transforming objects.GameObject#vertexRoundMode property:
"off" -- Never round."safe" -- Round only when the transform is position-only (no scale/rotation)."safeAuto" (default) -- Like "safe", but only when the camera has roundPixels enabled."full" -- Always round (can cause wobble on rotated sprites, PS1-style)."fullAuto" -- Like "full", but only when the camera has roundPixels enabled.TransformMatrix#setQuad roundPixels parameter has been removed.Mesh and Plane have been removed. These were limited 3D implementations; proper 3D support is planned for the future.The following have been completely removed:
phaser-ie9.js entry point (IE9 is no longer supported)Create.GenerateTexture and all Create Palettes / the create folder have been removed. TextureManager.generate is also removed as a result.
Math.SinCosTableGenerator has been removed.
All legacy polyfills removed:
Array.forEachArray.isArrayAudioContextMonkeyPatchconsoleMath.truncperformance.nowrequestAnimationFrameUint32ArrayModern browsers provide all of these natively.
The Spine 3 and Spine 4 plugins bundled with Phaser are no longer updated. Use the official Phaser Spine plugin created by Esoteric Software instead.
Shader#setTextures() now replaces the texture array rather than adding to it. If you were calling it multiple times to build up textures, call it once with the full array.DOMElement now throws an error if it has no container. Ensure your DOM elements have a parent container.GameObject#enableLighting can now be set even if the scene light manager is not enabled. The manager must still be enabled for lights to render, but the flag itself is no longer gated.Button class now accepts an isPressed parameter to initialize state correctly across scene transitions.BatchHandlerConfig#createOwnVertexBuffer type property has been removed.WebGLRenderer#genericVertexBuffer and #genericVertexData have been removed (freeing ~16MB RAM/VRAM).Use this checklist to track your migration progress:
npm install phaser@4 (or equivalent)BitmapMask usage with the Mask filterBloom, Shine, Circle, Gradient) with their Action/GameObject equivalentsColorMatrix calls (methods moved to .colorMatrix property)setTintFill() calls with setTint().setTintMode(Phaser.TintModes.FILL)Geom.Point usage with Vector2Math.TAU usage (now equals PI * 2, not PI / 2)Math.PI2 with Math.TAUPhaser.Struct.Set with native SetPhaser.Struct.Map with native Maprender() calls to DynamicTexture / RenderTexture usageCamera#matrix access to use the new matrix systemShader game objects to use new ShaderQuadConfig constructor#pragma directives)sprite.setPipeline('Light2D') with sprite.setLighting(true)z propertyTileSprite texture cropping codeGrid shape code (outline properties renamed to stroke)Mesh or Plane usageCreate.GenerateTexture or TextureManager.generateroundPixels behavior (now defaults to false)