The Phaser team has released v4.1.0, codenamed "Salusa", the first significant update since the Phaser 4 launch. The name is a nod to Salusa Secundus, the harsh prison world from Frank Herbert's Dune and the original home of House Corrino. There is also a hidden joke in there, courtesy of Phaser team member Ben Richards: Secundus means second in Latin, and this is indeed the second Phaser 4 release.
Wordplay aside, this release focuses on expanding rendering flexibility, resolving architectural inconsistencies in the Layer game object, and fixing a set of bugs in the ESM build that affected plugin developers and module-based projects. As with any community-driven release, many of these improvements were shaped by user feedback, so keep it coming 😉
Here is a guided tour of everything new in this release.
What's new in v4.1.0
- 🧱 Layer is now a GameObjectFull type compatibility and maintenance improvements across the board
- 🖼️ Mipmap RegenerationDynamic textures can now regenerate mipmaps on re-render, opt-in via config
- 📐 Filter Padding Rounding
New
getPaddingCeil()method resolves quality loss from fractional padding values
Layer is a GameObject
The Layer game object now properly extends GameObject, as you would intuitively expect. Previously, Layer existed in an awkward intersection: it needed to live in a scene like a game object, and contain children like a list, but technical constraints made full inheritance difficult. Multiple inheritance simply is not possible in this context.
The practical consequence was type incompatibility: a Layer could not be passed to any function that expected a GameObject. That is now fixed. The code change itself was straightforward, though the type declarations required multiple passes. During the process, the team also discovered a set of properties that had been added years ago but never appeared in the documentation. These are the properties inherited from EventEmitter, and they are now properly surfaced.
Layers must still sit at the top of a scene or inside other Layers. They cannot be children of Containers. That constraint remains unchanged.
Mipmap Regeneration for Dynamic Textures
Mipmaps improve rendering quality for textures that are scaled down. They require power-of-two dimensions and, until now, only worked reliably with static textures. Dynamic and render textures would not regenerate their mipmaps after updates, leading to blurry or degraded results when scaling.
Phaser v4.1.0 introduces RenderConfig#mipmapRegeneration, a new game config option. It is off by default for performance reasons, since regenerating mipmaps each frame has a cost and most textures are static. Setting it to true enables mipmap regeneration on DynamicTexture and RenderTexture objects when they re-render:
const config = {
type: Phaser.AUTO,
render: {
mipmapRegeneration: true
},
// ...
};
Note that filters do not support mipmaps, due to performance costs and the unpredictable sizing introduced by filter padding.
Filter Controller Padding Rounding
A new method, Controller#getPaddingCeil(), has been added. It returns the same value as getPadding(), but rounds up to the nearest integer. This resolves a subtle but visible quality regression that occurred when filter padding values were not whole numbers.
If you maintain code that calls getPadding() during filter rendering, it is recommended to replace those calls with getPaddingCeil() going forward. This change is primarily internal, but external plugin developers should take note.
Bug fixes
ESM Build Fixes
Phaser 4 changed its export configuration, which exposed several latent bugs in the ESM build, the JavaScript module version now used by all import Phaser from 'phaser' statements. Two issues in particular affected developers:
- ✅ There was no default export, forcing users to write
import * as Phaser from 'phaser'instead of the standard import syntax. - ✅ The
Classconstructor was missing from the ESM build, breaking a number of plugins that relied on it.
Both are now fixed. Note that by design, the ESM build does not add Phaser to the global window namespace, as modules should be encapsulated. If you need window.Phaser for legacy reasons, you can set it manually:
import Phaser from 'phaser';
window.Phaser = Phaser;
Other Fixes
- ✅ Regressions in rounded rectangle handling have been resolved. Thanks to@laineusfor the report.
- ✅ A duplicate function definition and exposed internal code documentation were removed from
RectangleCanvasRenderer. - ✅
RenderTexture#saveTextureno longer produces duplicate texture names. Thanks to@UnaiNeuronUp. - ✅
Utils.Array.GetRandomno longer returnsnullunexpectedly when onlystartIndexis specified.
How to upgrade
Updating to Phaser v4.1.0 is straightforward for most projects. Pull the latest version from npm:
npm install [email protected]
If you use the ESM build and were relying on import * as Phaser, you can now switch to the standard import Phaser from 'phaser' syntax. If you maintain a plugin that calls getPadding() directly, update those calls to getPaddingCeil() to benefit from the improved rounding behavior.
Build Phaser games faster with a visual editor
Phaser Editor v5 is the dedicated IDE for Phaser game development. Design scenes visually, manage assets, write scripts, and compile, all without leaving your editor. Fully compatible with Phaser v4, including all features introduced in this release.
