Animate PixiJS Filter Properties Over Time

This article explains how to animate the internal properties and custom uniforms of filters in Pixi.js using the application loop. You will learn how to set up an animation ticker, access filter properties, and update them incrementally over time to create dynamic visual effects like pulsing blurs or custom shader distortions.

Using the PixiJS Ticker

The standard way to animate properties in Pixi.js is through the Ticker class. The ticker runs an update loop on every frame (typically 60 frames per second) and provides a delta time value to ensure smooth animations regardless of frame rate fluctuations.

To animate a filter, you instantiate the filter, apply it to a container or sprite, and then modify the filter’s properties inside the ticker listener.

Animating Built-in Filters

For built-in filters, you can directly access and modify their public properties (such as blur, noise, or red) inside the loop.

import * as PIXI from 'pixi.js';

// 1. Initialize the PixiJS Application
const app = new PIXI.Application();
await app.init({ width: 800, height: 600 });
document.body.appendChild(app.canvas);

// 2. Create a sprite and apply a BlurFilter
const sprite = PIXI.Sprite.from('path/to/image.png');
const blurFilter = new PIXI.BlurFilter();
sprite.filters = [blurFilter];
app.stage.addChild(sprite);

// 3. Create an animation variable
let elapsed = 0;

// 4. Update the filter property in the ticker loop
app.ticker.add((ticker) => {
    // Increment elapsed time based on delta time
    elapsed += ticker.deltaTime * 0.05;
    
    // Animate the blur property using a sine wave for a pulsing effect
    blurFilter.blur = (Math.sin(elapsed) + 1) * 10; 
});

Animating Custom Filter Uniforms

If you are using a custom shader (via PIXI.Filter or PIXI.Shader), you animate the internal properties by updating the filter’s uniforms object. This is highly useful for passing a time variable to a fragment shader.

// Define custom fragment shader code
const fragmentShader = `
    precision mediump float;
    varying vec2 vTextureCoord;
    uniform sampler2D uSampler;
    uniform float uTime;

    void main(void) {
        vec2 uv = vTextureCoord;
        // Create a waving wave effect using the uTime uniform
        uv.x += sin(uv.y * 10.0 + uTime) * 0.02;
        gl_FragColor = texture2D(uSampler, uv);
    }
`;

// Create a custom filter with default uniforms
const customFilter = new PIXI.Filter({
    glProgram: PIXI.GlProgram.from({
        vertex: PIXI.defaultVertex,
        fragment: fragmentShader,
    }),
    resources: {
        timeUniforms: {
            uTime: { value: 0, type: 'f32' },
        }
    }
});

sprite.filters = [customFilter];

let totalTime = 0;

app.ticker.add((ticker) => {
    // Increment time
    totalTime += ticker.deltaTime * 0.02;
    
    // Update the custom uniform inside the shader
    customFilter.resources.timeUniforms.uTime = totalTime;
});

By utilizing PixiJS’s app.ticker, you can smoothly transition any internal filter value, enabling complex visual states and rendering effects.