pixijs-ticker — Claude Code skill: PixiJS v8 render loop control
A skill for per-frame logic in PixiJS v8: ticker callbacks, time units, priority ordering and frame rate capping.
npx skills add pixijs/pixijs-skills --skill pixijs-ticker -a claude-codeWhat pixijs-ticker does
Reach for this skill when you write per-frame logic or take over the PixiJS v8 render loop. It covers Ticker.add, addOnce and remove, the difference between deltaTime, deltaMS and elapsedMS, callback ordering through UPDATE_PRIORITY, maxFPS and minFPS capping, speed scaling, Ticker.shared versus a custom instance, the per-object onRender hook and manual rendering.
The trap worth knowing up front: deltaTime is not milliseconds. It equals deltaMS * Ticker.targetFPMS, with targetFPMS at 0.06, so 60 fps gives 1.0 and 30 fps gives 2.0. Treat it as a time value and your animation runs at double speed on a slow machine. Use deltaMS for pixels-per-second math, and keep elapsedMS for profiling since it is neither speed-scaled nor capped by minFPS.
Priority decides who runs first: INTERACTION (50), HIGH (25), NORMAL (0), LOW (-25), UTILITY (-50). app.render() sits at LOW via TickerPlugin, so physics on HIGH lands before drawing. Pass a class method with its context as the second argument, and repeat both function and context in remove — otherwise the callback stays registered. Ticker.shared and Ticker.system are protected from destroy(), while new Ticker() starts with autoStart = false and you own the lifecycle.
What it helps with
- run logic on every frame
- pick between deltaTime, deltaMS and elapsedMS for animation
- order physics before rendering with UPDATE_PRIORITY
- cap the frame rate on weaker hardware
- tie updates to one display object via onRender
Example prompts for the agent
- Add sprite rotation through app.ticker.add using deltaTime so speed does not depend on frame rate
- Register physics updates at UPDATE_PRIORITY.HIGH so they run before app.render()
- Set app.ticker.maxFPS to 30 and explain what that does to deltaMS
How to install pixijs-ticker
1Skills CLI (Vercel)
npx skills add pixijs/pixijs-skills --skill pixijs-ticker -a claude-code2Manually: put the files in place
~/.claude/skills/pixijs-ticker/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/pixijs-ticker/
1Skills CLI (Vercel)
npx skills add pixijs/pixijs-skills --skill pixijs-ticker -a codex2Manually: put the files in place
.agents/skills/pixijs-ticker/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add pixijs/pixijs-skills --skill pixijs-ticker -a cursor2Manually: put the files in place
.cursor/skills/pixijs-ticker/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install pixijs/pixijs-skills pixijs-ticker2Manually: put the files in place
.github/skills/pixijs-ticker/SKILL.md1Skills CLI (Vercel)
npx skills add pixijs/pixijs-skills --skill pixijs-ticker -a gemini-cli2Platform CLI
gemini skills install https://github.com/pixijs/pixijs-skills --path skills/pixijs-tickerManually: put the files in place
.devin/skills/pixijs-ticker/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add pixijs/pixijs-skills --skill pixijs-ticker -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about pixijs-ticker
How do I install pixijs-ticker in Claude?
Run npx skills add pixijs/pixijs-skills --skill pixijs-ticker -a claude-code. The skill lands in ~/.claude/skills/pixijs-ticker/SKILL.md.
What does the pixijs-ticker skill do?
It documents the PixiJS v8 render loop: ticker callbacks, time units, priorities, frame rate capping and manual rendering. It keeps you from mistaking deltaTime for milliseconds.
How is Ticker.shared different from a new Ticker instance?
Ticker.shared is a singleton with autoStart = true, protected from destroy(). new Ticker() starts with autoStart = false, and stopping or destroying it is on you.
Source text
SKILL.md · MIT
---
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
```ts
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 |
```ts
import { Application } from "pixi.js";
const app = new Application();
await app.init({ width: 800, height: 600 });
app.ticker.add((ticker) => {
// deltaTime: dimensionless scalar, ~1.0 at 60fps
sprite.rotation += 0.1 * ticker.deltaTime;
// deltaMS: real milliseconds (speed-scaled, capped)
sprite.x += (200 / 1000) * ticker.deltaMS; // 200 pixels per second
// elapsedMS: raw milliseconds (no scaling, no cap)
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
```ts
import { Application, UPDATE_PRIORITY } from "pixi.js";
const app = new Application();
await app.init({ width: 800, height: 600 });
// INTERACTION (50) > HIGH (25) > NORMAL (0) > LOW (-25) > UTILITY (-50)
// app.render() is registered at LOW by the TickerPlugin
app.ticker.add(
(ticker) => {
// Physics runs before normal-priority callbacks
updatePhysics(ticker.deltaMS);
},
undefined,
UPDATE_PRIORITY.HIGH,
);
app.ticker.add((ticker) => {
// Default priority (NORMAL = 0), runs after HIGH but before render
updateAnimations(ticker.deltaTime);
});
// Pass `this` as the second argument to preserve context on class methods
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); // must match both fn and context
```
### Frame rate capping
```ts
import { Ticker } from "pixi.js";
const ticker = new Ticker();
ticker.maxFPS = 30; // Cap at 30fps (skips frames to maintain interval)
ticker.minFPS = 10; // Cap deltaTime so it never exceeds 10fps worth
// If maxFPS < minFPS, minFPS is lowered to match
// If minFPS > maxFPS, maxFPS is raised to match
```
`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
```ts
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
```ts
import { Ticker, UPDATE_PRIORITY } from "pixi.js";
// Ticker.shared: singleton, autoStart=true, protected from destroy()
const shared = Ticker.shared;
// Ticker.system: separate instance used by engine background tasks,
// independent of the main scene ticker. It's a plain Ticker instance
// (autoStart=true, _protected=true) with no intrinsic priority; listeners
// are typically added at UTILITY priority by convention.
const system = Ticker.system;
// new Ticker(): custom instance, autoStart=false, you manage the lifecycle
const custom = new Ticker();
custom.autoStart = true; // start when first listener is added
custom.add((ticker) => {
console.log(ticker.deltaMS);
});
// One-shot callback that auto-removes after firing
custom.addOnce(() => console.log("fires once"), null, UPDATE_PRIORITY.NORMAL);
// When done:
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
```ts
import { Application } from "pixi.js";
const app = new Application();
await app.init({ autoStart: false });
// Pause and resume the built-in render loop at any time.
app.start();
app.stop();
// Or drive the loop yourself (headless, visibility-gated, fixed timestep, etc.)
function animate() {
app.ticker.update(); // fires registered callbacks
app.render(); // renders the stage
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
```ts
import { Application } from "pixi.js";
const app = new Application();
await app.init({ width: 800, height: 600 });
app.ticker.speed = 0.5; // Half speed (slow motion)
app.ticker.speed = 2.0; // Double speed
// speed affects deltaTime and deltaMS, but NOT elapsedMS
```
## Common Mistakes
### [CRITICAL] Ticker callback expects delta as first argument
Wrong:
```ts
app.ticker.add((dt) => {
bunny.rotation += dt;
});
```
Correct:
```ts
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:
```ts
class MySprite extends Sprite {
updateTransform() {
super.updateTransform();
this.rotation += 0.01;
}
}
```
Correct:
```ts
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:
```ts
app.ticker.add((ticker) => {
// Tries to move 100px/sec but deltaTime is ~1.0, not ~16.67
sprite.x += (100 * ticker.deltaTime) / 1000;
});
```
Correct:
```ts
app.ticker.add((ticker) => {
// Using deltaMS for time-based movement
sprite.x += (100 / 1000) * ticker.deltaMS;
// Or using deltaTime as a frame-rate multiplier
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
- [Ticker](https://pixijs.download/release/docs/ticker.Ticker.html.md)
- [TickerPlugin](https://pixijs.download/release/docs/app.TickerPlugin.html.md)
- [TickerPluginOptions](https://pixijs.download/release/docs/app.TickerPluginOptions.html.md)
© pixijs · MIT · pixijs/pixijs-skills