| name | pixijs-ticker |
| description | Use this skill when running per-frame logic or controlling the PixiJS v8 render loop. Covers Ticker.add/addOnce/remove, deltaTime vs deltaMS vs elapsedMS, UPDATE_PRIORITY ordering, maxFPS/minFPS capping, speed scaling, Ticker.shared vs new instances, per-object onRender hook, manual rendering. Triggers on: Ticker, UPDATE_PRIORITY, deltaTime, deltaMS, elapsedMS, onRender, app.ticker, maxFPS, minFPS, Ticker.shared. |
| license | MIT |
app.ticker runs registered callbacks every frame and drives app.render() at UPDATE_PRIORITY.LOW. Each callback receives the Ticker instance; read deltaTime as a frame-rate-independent multiplier (≈1.0 at 60fps) or deltaMS for real-time calculations.
Quick Start
app.ticker.add((ticker) => {
sprite.rotation += 0.01 * ticker.deltaTime;
sprite.x += (200 / 1000) * ticker.deltaMS;
});
app.ticker.add(
(ticker) => {
updatePhysics(ticker.deltaMS);
},
undefined,
UPDATE_PRIORITY.HIGH,
);
app.ticker.maxFPS = 30;
app.ticker.speed = 0.5;
sprite.onRender = () => {
sprite.scale.x = Math.sin(performance.now() / 500);
};
Related skills: pixijs-application (Application setup and sharedTicker option), pixijs-performance (frame rate optimization), pixijs-migration-v8 (v7 ticker signature changes).
Core Patterns
Time units
The Ticker exposes three timing values, each for different use cases:
| Property | Type | Scaled by speed? | Capped by minFPS? | Use case |
|---|
deltaTime | dimensionless (~1.0 at 60fps) | yes | yes | Frame-rate-independent animation multipliers |
deltaMS | milliseconds | yes | yes | Time-based calculations (pixels/sec) |
elapsedMS | milliseconds | no | no | Raw measurement, profiling |
import { Application } from "pixi.js";
const app = new Application();
await app.init({ width: 800, height: 600 });
app.ticker.add((ticker) => {
sprite.rotation += 0.1 * ticker.deltaTime;
sprite.x += (200 / 1000) * ticker.deltaMS;
console.log(`Raw frame time: ${ticker.elapsedMS}ms`);
});
Tension note: deltaTime is not milliseconds. It is deltaMS * Ticker.targetFPMS where targetFPMS is 0.06 (i.e. 1/16.67). At exactly 60fps, deltaTime is 1.0. At 30fps, deltaTime is 2.0. This catches people who treat it as a time value.
Priority ordering and context binding
import { Application, UPDATE_PRIORITY } from "pixi.js";
const app = new Application();
await app.init({ width: 800, height: 600 });
app.ticker.add(
(ticker) => {
updatePhysics(ticker.deltaMS);
},
undefined,
UPDATE_PRIORITY.HIGH,
);
app.ticker.add((ticker) => {
updateAnimations(ticker.deltaTime);
});
class GameSystem {
public speed = 5;
public position = 0;
public update(ticker: Ticker): void {
this.position += this.speed * ticker.deltaTime;
}
}
const system = new GameSystem();
app.ticker.add(system.update, system);
app.ticker.remove(system.update, system);
Frame rate capping
import { Ticker } from "pixi.js";
const ticker = new Ticker();
ticker.maxFPS = 30;
ticker.minFPS = 10;
maxFPS skips update calls to enforce a ceiling. minFPS caps deltaTime/deltaMS so large frame drops don't produce enormous deltas (default minFPS is 10).
Per-object onRender hook
import { Sprite, Assets, Application } from "pixi.js";
const app = new Application();
await app.init({ width: 800, height: 600 });
const texture = await Assets.load("bunny.png");
const sprite = new Sprite(texture);
app.stage.addChild(sprite);
sprite.onRender = (renderer) => {
sprite.rotation += 0.01;
};
onRender is called during scene graph traversal, before GPU rendering. It is an alternative to a global ticker callback when logic is tied to a specific display object.
Ticker.shared, Ticker.system, and new Ticker
import { Ticker, UPDATE_PRIORITY } from "pixi.js";
const shared = Ticker.shared;
const system = Ticker.system;
const custom = new Ticker();
custom.autoStart = true;
custom.add((ticker) => {
console.log(ticker.deltaMS);
});
custom.addOnce(() => console.log("fires once"), null, UPDATE_PRIORITY.NORMAL);
custom.stop();
custom.destroy();
Application creates its own Ticker by default. Set sharedTicker: true in app.init() to use Ticker.shared instead. Ticker.shared and Ticker.system are both _protected and will not actually be destroyed if you call destroy() on them. Read ticker.FPS for the measured frame rate and ticker.count for the current listener count.
App lifecycle and manual rendering
import { Application } from "pixi.js";
const app = new Application();
await app.init({ autoStart: false });
app.start();
app.stop();
function animate() {
app.ticker.update();
app.render();
requestAnimationFrame(animate);
}
animate();
app.start() and app.stop() are added by the TickerPlugin and map to ticker.start() / ticker.stop(). Use autoStart: false plus your own frame driver when you need to pause on tab blur, run a fixed-timestep loop, or render offscreen.
Speed scaling
import { Application } from "pixi.js";
const app = new Application();
await app.init({ width: 800, height: 600 });
app.ticker.speed = 0.5;
app.ticker.speed = 2.0;
Common Mistakes
[CRITICAL] Ticker callback expects delta as first argument
Wrong:
app.ticker.add((dt) => {
bunny.rotation += dt;
});
Correct:
app.ticker.add((ticker) => {
bunny.rotation += ticker.deltaTime;
});
v8 passes the Ticker instance as the callback argument, not a delta number. The v7 pattern (dt) => ... compiles but dt is the entire Ticker object, so arithmetic on it produces NaN.
[HIGH] Using updateTransform for per-frame logic
Wrong:
class MySprite extends Sprite {
updateTransform() {
super.updateTransform();
this.rotation += 0.01;
}
}
Correct:
class MySprite extends Sprite {
constructor() {
super();
this.onRender = this._onRender.bind(this);
}
private _onRender() {
this.rotation += 0.01;
}
}
updateTransform was removed in v8. Use the onRender callback for per-object per-frame logic.
[MEDIUM] Treating deltaTime as milliseconds
Wrong:
app.ticker.add((ticker) => {
sprite.x += (100 * ticker.deltaTime) / 1000;
});
Correct:
app.ticker.add((ticker) => {
sprite.x += (100 / 1000) * ticker.deltaMS;
sprite.x += 1.5 * ticker.deltaTime;
});
deltaTime is a dimensionless scalar (~1.0 at 60fps), not milliseconds. Use deltaMS for real time calculations. Use deltaTime as a simple multiplier when you want "per frame at 60fps" behavior.
API Reference