Loading...
Loading...
Use this skill when learning about new features, game objects, components, and rendering capabilities added in Phaser 4. Covers Filters, RenderNodes, CaptureFrame, Gradient, Noise, SpriteGPULayer, TilemapGPULayer, Lighting component, RenderSteps, and new tint modes. Triggers on: new in v4, Phaser 4 features, RenderNode, SpriteGPULayer, CaptureFrame, Gradient game object, Noise game object, new tint modes. For migrating v3 code to v4, see the v3-to-v4-migration skill instead.
npx skill4agent add phaserjs/phaser v4-new-featuresNew features and capabilities in Phaser 4: Filters (replacing FX/BitmapMask), RenderNodes (replacing Pipelines), CaptureFrame, Gradient, Noise game objects, SpriteGPULayer, TilemapGPULayer, Lighting component, RenderSteps, and new tint modes.
Migrating from v3? See the v3 to v4 Migration Guide for step-by-step code changes, removed APIs, and a migration checklist.
| v3 Feature | v4 Replacement |
|---|---|
| |
FX ( | Filters ( |
| |
| |
| Derived FX: Bloom, Circle, Gradient, Shine | Actions ( |
| Removed (proper 3D planned for future) |
| Use |
CaptureFrameGradientNoiseNoiseCell2D/3D/4DNoiseSimplex2D/3DSpriteGPULayerStampTilemapGPULayerLightingRenderStepsRenderNodesMULTIPLYFILLADDSCREENOVERLAYHARD_LIGHTFull reference:filters-and-postfx.md
// v3 approach (FX):
sprite.preFX.addGlow(0xff00ff, 4);
sprite.postFX.addBlur(0, 2, 2, 1);
// v4 approach (Filters):
sprite.enableFilters();
sprite.filters.internal.addGlow(0xff00ff, 4, 0, 1);
sprite.filters.external.addBlur(0, 2, 2, 1);
// v3 approach (BitmapMask):
const mask = new Phaser.Display.Masks.BitmapMask(scene, maskImage);
sprite.setMask(mask);
// v4 approach (FilterMask):
sprite.enableFilters();
sprite.filters.internal.addMask(maskImage);PipelineRenderNoderun()batch()RenderNodeManager// RenderNode roles on a game object:
// - 'Submitter': runs other node roles for each element
// - 'Transformer': provides vertex coordinates
// - 'Texturer': handles textures
// GameObjects have default and custom render node maps:
gameObject.defaultRenderNodes // built-in nodes per role
gameObject.customRenderNodes // overrides per role
gameObject.renderNodeData // data keyed by node name// Override a specific render role:
gameObject.setRenderNodeRole('Submitter', 'MyCustomSubmitter');
// Pass data to a render node:
gameObject.setRenderNodeRole('Transformer', 'MyTransformer', {
customProperty: 42
});
// Remove a custom node (falls back to default):
gameObject.setRenderNodeRole('Submitter', null);BatchHandlerQuadBatchHandlerQuadSingleBatchHandlerTileSpriteBatchHandlerTriFlatBatchHandlerPointLightBatchHandlerStripSubmitterQuadSubmitterTileSubmitterTileSpriteSubmitterSpriteGPULayerSubmitterTilemapGPULayerTransformerImageTransformerStampTransformerTileTransformerTileSpriteTexturerImageTexturerTileSpritefilters-and-postfx.mdBaseFilterBaseFilterShaderFilterBarrelFilterBlendFilterBlockyFilterBlurFilterBokehFilterColorMatrixFilterCombineColorMatrixFilterDisplacementFilterGlowFilterGradientMapFilterImageLightFilterKeyFilterMaskFilterNormalToolsFilterPanoramaBlurFilterParallelFiltersFilterPixelateFilterQuantizeFilterSamplerFilterShadowFilterThresholdFilterVignetteFilterWipeCameraFillCameraFillRectFillPathFillTriDrawLineStrokePathShaderQuadListCompositorRebindContextYieldContextDynamicTextureHandler// Register a custom node constructor:
renderer.renderNodes.addNodeConstructor('MyNode', MyNodeClass);
// Or add a pre-built node instance:
renderer.renderNodes.addNode('MyNode', myNodeInstance);// Everything above this in the display list gets captured:
const image1 = this.add.image(400, 300, 'background');
// Enable framebuffer usage on the camera:
this.cameras.main.setForceComposite(true);
// Create the capture point:
const capture = this.add.captureFrame('myCapturedTexture');
// Use the captured texture on another object:
const overlay = this.add.image(400, 300, 'myCapturedTexture');
// Add filters to the overlay to distort the captured scenecamera.setForceComposite(true)visible = falsesrc/gameobjects/captureframe/CaptureFrame.jsShaderColorRampColorBand// Simple linear gradient:
const grad = this.add.gradient(undefined, 100, 100, 200, 200);
// Complex radial gradient with multiple color bands:
const halo = this.add.gradient({
bands: [
{ start: 0.5, end: 0.6, colorStart: [0.5, 0.5, 1, 0], colorEnd: 0xffffff, colorSpace: 1, interpolation: 4 },
{ start: 0.6, end: 1, colorStart: 0xffffff, colorEnd: [1, 0.5, 0.5, 0], colorSpace: 1, interpolation: 3 }
],
dither: true,
repeatMode: 1,
shapeMode: 2, // radial
start: { x: 0.5, y: 0.5 },
shape: { x: 0.5, y: 0.0 }
}, 400, 300, 800, 800);
// Animate:
halo.offset = 0.1 * (1 + Math.sin(time / 1000));GradientQuadConfigbandsshapeModerepeatModestartshapeditherColorRampColorBandgradient.ramp.encode()src/gameobjects/gradient/Gradient.jsShader| Type | Factory | Description |
|---|---|---|
| | White noise (random hash-based) |
| | 2D cellular/Worley/Voronoi noise |
| | 3D cellular noise (Z-axis slicing for animation) |
| | 4D cellular noise (Z+W axis slicing) |
| | 2D simplex/gradient noise (clouds, fire, water) |
| | 3D simplex noise |
// Basic white noise:
const noise = this.add.noise({
noiseOffset: [0, 0],
noisePower: 1
}, 100, 100, 256, 256);
// Cellular noise with customization:
const cells = this.add.noiseCell2D({
noiseOffset: [0, 0],
noiseIterations: 3,
noiseNormalMap: true // output as normal map for lighting
}, 200, 200, 256, 256);
// Simplex noise for natural effects:
const simplex = this.add.noiseSimplex2D({
noiseFlow: 0, // animate this for evolution
noiseIterations: 4,
noiseWarpAmount: 0.5, // turbulence
noiseSeed: 42,
noiseNormalMap: false
}, 300, 300, 256, 256);noiseOffset[x, y]noisePowernoiseNormalMapnoiseIterationsPhaser.Math.Hash()Phaser.Math.HashCell()Phaser.Math.HashSimplex()src/gameobjects/noise/const layer = this.add.spriteGPULayer(texture, size); // size = max number of members
// Add members (do this all at once, not incrementally):
const member = { x: 100, y: 200, frame: 'tree', scaleX: 1, scaleY: 1, alpha: 1 };
layer.addMember(member);
// Reuse the member object for efficiency with millions of entries:
member.x = 300;
member.y = 400;
member.frame = 'bush';
layer.addMember(member);
// Enable lighting on the layer:
layer.setLighting(true);scaleX/scaleY/alphasrc/gameobjects/spritegpulayer/SpriteGPULayer.jsFull component reference:game-object-components.md
// v3 approach:
sprite.setPipeline('Light2D');
// v4 approach:
sprite.setLighting(true);
// Self-shadowing (simulates surface shadows from texture brightness):
sprite.setSelfShadow(true, 0.5, 1/3);
// Args: enabled, penumbra (lower = sharper), diffuseFlatThreshold (0-1)
// Use game-wide default for self-shadow:
sprite.setSelfShadow(null); // reads from config.render.selfShadowsrc/gameobjects/components/Lighting.js// Add a custom render step:
gameObject.addRenderStep(function (renderer, gameObject, drawingContext, parentMatrix, renderStep, displayList, displayListIndex) {
// Custom rendering logic here
// Call next step when ready:
var nextFn = gameObject._renderSteps[renderStep + 1];
if (nextFn) {
nextFn(renderer, gameObject, drawingContext, parentMatrix, renderStep + 1, displayList, displayListIndex);
}
});_renderStepsrenderWebGLStep()renderWebGLsrc/gameobjects/components/RenderSteps.jsdefaultRenderNodescustomRenderNodesrenderNodeDatasrc/gameobjects/components/RenderNodes.jsFull tilemap reference:tilemaps.md
// Create via Tilemap with the gpu flag:
const map = this.make.tilemap({ key: 'level1' });
const tileset = map.addTilesetImage('tiles', 'tilesImage');
const gpuLayer = map.createLayer('Ground', tileset, 0, 0, true); // last arg: gpu = truegenerateLayerDataTexture()src/tilemaps/TilemapGPULayer.js