Dynamic Graphics Masking in PixiJS
In Pixi.js, using a dynamically drawn Graphics object as
a visibility mask allows you to create complex, real-time masking
effects like spotlights, revealing transitions, or custom clip paths.
This guide will walk you through the step-by-step process of creating a
Graphics object, drawing shapes onto it dynamically, and
applying it as a mask to any Pixi.js Container or
Sprite.
Step 1: Create the Target and the Mask
To begin, you need a target display object (such as a Sprite) that
you want to mask, and a Graphics object that will serve as
the mask itself.
import { Application, Sprite, Graphics } from 'pixi.js';
// Initialize Pixi.js Application
const app = new Application({ width: 800, height: 600 });
document.body.appendChild(app.view);
// Create the target sprite to be masked
const background = Sprite.from('path/to/your/image.jpg');
app.stage.addChild(background);
// Create the Graphics object that will act as the mask
const maskGraphics = new Graphics();
// Assign the graphics object as the mask of the background sprite
background.mask = maskGraphics;
// Crucial: Add the mask to the stage so Pixi.js can render it
app.stage.addChild(maskGraphics);Step 2: Dynamically Draw and Update the Mask
To make the mask dynamic, you must clear and redraw the geometry inside your game loop or in response to user input (such as mouse movements).
In Pixi.js, the color used to draw the mask shape does not matter; only the shape’s alpha channel and geometry determine what remains visible.
// Enable interactivity on the stage
app.stage.interactive = true;
app.stage.hitArea = app.screen;
// Update the mask position based on pointer movement
app.stage.on('pointermove', (event) => {
const { x, y } = event.global;
// Clear the previous frame's drawing
maskGraphics.clear();
// Redraw the shape at the new pointer coordinates
maskGraphics.beginFill(0xffffff);
maskGraphics.drawCircle(x, y, 100); // Draw a circular mask with a 100px radius
maskGraphics.endFill();
});Step 3: Animating the Mask
If you want to animate the mask independently without user input, you can update its scale, position, or redraw its geometry inside the Pixi.js ticker.
let radius = 50;
let growing = true;
app.ticker.add(() => {
// Pulse the radius size
if (growing) {
radius += 2;
if (radius > 200) growing = false;
} else {
radius -= 2;
if (radius < 50) growing = true;
}
// Clear and redraw the circle in the center of the screen
maskGraphics.clear();
maskGraphics.beginFill(0xffffff);
maskGraphics.drawCircle(400, 300, radius);
maskGraphics.endFill();
});Key Considerations
- The Scene Graph: Always remember to add the
Graphicsmask to the stage usingapp.stage.addChild(maskGraphics). If the mask is not part of the active scene graph, it will not render, and the masking effect will fail. - Complex Shapes: You can chain multiple drawing
commands (like
.drawRect(),.drawCircle(), or path-based drawing) inside a singleGraphicsobject to create complex, compound masks. - Performance: Calling
.clear()and redrawing every frame is highly performant in Pixi.js because WebGL handles the dynamic geometry generation efficiently.