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