How Pixi.js Masks Work to Hide Display Objects
In Pixi.js, masking is a powerful technique used to restrict the visibility of a display object to a specific shape or area. This article explains the underlying mechanics of how masks work in Pixi.js, distinguishes between the two primary types of masks—graphics and sprite masks—and outlines how to implement them efficiently in your WebGL applications.
The Core Concept of Masking
At its core, a mask in Pixi.js is a display object applied to another
display object (or container) via the .mask property. When
a mask is applied, Pixi.js modifies the rendering pipeline so that only
the portions of the target object that overlap with the geometry or
alpha channel of the mask are drawn to the screen.
To apply a mask, you assign the mask object directly to the target’s mask property:
targetDisplayObject.mask = maskObject;To remove the mask and restore full visibility, you simply set the property back to null:
targetDisplayObject.mask = null;Types of Masks in Pixi.js
Pixi.js supports two main types of masks, each utilizing a different rendering technique depending on the type of display object used as the mask.
1. Graphics Masks (Geometry-Based)
Graphics masks use a PIXI.Graphics object to define the
visible area.
- How it works: Under the hood, Pixi.js utilizes the WebGL stencil buffer or scissor test. When rendering, Pixi.js draws the vector geometry of the graphics mask into the stencil buffer to create a template. It then draws the target display object, only keeping the pixels that pass the stencil test (i.e., those inside the drawn geometry).
- Best used for: Sharp geometric shapes like circles, rectangles, stars, or custom polygons.
- Performance: Extremely fast and memory-efficient because it relies on hardware-level stencil operations without requiring extra textures.
2. Sprite Masks (Alpha-Based)
Sprite masks use a PIXI.Sprite (an image or texture) to
define the visible area.
- How it works: This method relies on the alpha channel (transparency) of the sprite’s texture. Pixi.js uses a special shader that multiplies the alpha value of the target display object’s pixels by the alpha value of the corresponding pixels in the mask sprite. If a pixel in the mask sprite is 100% opaque, the target pixel is fully visible; if it is transparent, the target pixel is hidden.
- Best used for: Complex shapes, soft edges, gradients, and organic feathered transitions that cannot be easily achieved with vector geometry.
- Performance: More resource-intensive than graphics masks. To calculate alpha blending, Pixi.js must use render textures and apply a filter pass, which can impact performance if overused or applied to large areas.
Key Rules and Best Practices
To ensure masks render correctly and perform well in your project, keep the following rules in mind:
- Adding to the Stage: While a mask will function if
it is just assigned to the
.maskproperty, it is highly recommended to add the mask object to the stage (the display tree). If the mask is not in the display tree, it cannot inherit transformations like scaling, rotation, or position updates relative to its parent. - Interaction and Hit Areas: Masked areas do not
automatically restrict user interactions (like clicks or taps) to the
visible shape. If you need button interactions to only trigger within
the visible masked area, you must manually define a
hitAreaon the interactive object. - Nested Masking: Pixi.js supports nested masks (a masked object inside another masked container). However, deep nesting can quickly degrade performance, especially when mixing graphics and sprite masks.