
Pixijs Core Concepts
- 3.1k installs
- 293 repo stars
- Updated June 4, 2026
- pixijs/pixijs-skills
pixijs-core-concepts explains PixiJS v8 renderer backends, the render loop, systems-and-pipes architecture, and environment adapters.
About
PixiJS Core Concepts explains how PixiJS v8 puts pixels on screen through renderer selection, the per-frame render loop, and environment adaptation. autoDetectRenderer picks WebGLRenderer, WebGPURenderer, or CanvasRenderer based on a preference array such as webgpu then webgl, with WebGPU fastest where supported and WebGL as fallback. The TickerPlugin registers renderer.render at UPDATE_PRIORITY.LOW while user callbacks at NORMAL or HIGH run first for physics or game logic. Renderers combine Systems for textures, buffers, filters, and masks with RenderPipes per renderable type like sprites, graphics, and text. DOMAdapter abstracts canvas creation, image loading, and fetch for Web Workers or SSR when set before Application.init. The skill documents common mistakes including accessing app.renderer before await init, setting DOMAdapter too late, and treating preference as a guarantee without checking renderer.name. Related skills cover Application setup, ticker priorities, environments, custom RenderPipes, and scene graph basics.
- autoDetectRenderer chooses webgl, webgpu, or canvas with preference hints not guarantees.
- TickerPlugin drives renderer.render at LOW priority after NORMAL and HIGH callbacks.
- Renderer architecture splits Systems lifecycle services from RenderPipes per renderable type.
- DOMAdapter must be set before Application.init for Workers, SSR, or strict CSP.
- Common mistakes cover async init, late adapter swaps, and backend-specific feature branching.
Pixijs Core Concepts by the numbers
- 3,130 all-time installs (skills.sh)
- +224 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #155 of 2,277 Frontend Development skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
pixijs-core-concepts capabilities & compatibility
- Capabilities
- webgl, webgpu, and canvas backend selection guid · ticker priority ordering relative to render call · systems vs renderpipes mental model · domadapter setup for worker and ssr targets · async application.init prerequisite enforcement · backend specific feature gating via renderer.nam
- Use cases
- frontend
npx skills add https://github.com/pixijs/pixijs-skills --skill pixijs-core-conceptsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 3.1k |
|---|---|
| repo stars | ★ 293 |
| Security audit | 3 / 3 scanners passed |
| Last updated | June 4, 2026 |
| Repository | pixijs/pixijs-skills ↗ |
How does PixiJS v8 choose a GPU backend, schedule frames, and adapt to Workers or SSR contexts?
Understand PixiJS v8 rendering: backend selection, render loop, systems-and-pipes architecture, and environment adapters.
Who is it for?
Developers learning PixiJS v8 rendering fundamentals before Application, ticker, or custom pipe work.
Skip if: Skip when you only need scene graph Container basics; use pixijs-scene-core-concepts instead.
When should I use this skill?
User asks about PixiJS renderer, WebGL, WebGPU, render loop, systems, pipes, autoDetectRenderer, or environments.
What you get
Correct Application.init flow, renderer.name checks, ticker priority ordering, and pre-init DOMAdapter setup.
- Renderer backend decision
- Render loop integration plan
- Environment adapter configuration
By the numbers
- Covers 3 PixiJS v8 renderer backends: WebGLRenderer, WebGPURenderer, and CanvasRenderer
Files
Foundational model for how PixiJS v8 gets pixels on the screen: the renderer decides which GPU backend to use, the render loop drives per-frame work, and the environment layer adapts the library to browser, Web Worker, or SSR contexts. For the scene graph itself (Containers, transforms, destroy), see pixijs-scene-core-concepts.
Quick Start
console.log(app.renderer.name); // 'webgl' | 'webgpu' | 'canvas'
app.ticker.add((ticker) => {
sprite.rotation += 0.01 * ticker.deltaTime;
});
const tex = app.renderer.extract.texture({ target: app.stage });
app.renderer.render({ container: app.stage });app.renderer is the WebGLRenderer, WebGPURenderer, or CanvasRenderer chosen by autoDetectRenderer. The TickerPlugin drives renderer.render() automatically; call it manually only with autoStart: false. Backend selection happens in Application.init({ preference }); see pixijs-application for setup.
Related skills: pixijs-application (Application construction and lifecycle), pixijs-ticker (per-frame logic, priorities, FPS capping), pixijs-environments (Web Worker, SSR, strict CSP), pixijs-custom-rendering (writing a RenderPipe), pixijs-scene-core-concepts (scene graph basics).
Topics
| Topic | Reference | When |
|---|---|---|
| Choosing a backend | references/renderers.md | Preference forms, per-renderer options, systems and pipes |
| Per-frame execution | references/render-loop.md | Priority order, time units, manual rendering |
For deep dives into any single topic, open the corresponding reference file. Non-browser targets (DOMAdapter, WebWorkerAdapter, custom adapters, strict CSP) are covered in the pixijs-environments skill.
Decision guide
- Setting up an Application? Start with
pixijs-application. This skill explains what the renderer does under the hood. - Choosing between WebGL and WebGPU? Use
['webgpu', 'webgl']as your preference array. WebGPU is fastest where available; WebGL is the reliable fallback. Seereferences/renderers.md. - Running in a Web Worker? Set
DOMAdapter.set(WebWorkerAdapter)beforeapp.init. See thepixijs-environmentsskill for complete setup. - Need manual control over when rendering happens? Set
autoStart: falseand callapp.renderer.render(app.stage)from your own loop. Seereferences/render-loop.md. - Integrating with a physics library? Add your update at
UPDATE_PRIORITY.HIGHso physics runs before the render atLOW. Seereferences/render-loop.md. - Writing a custom renderable? Implement a
RenderPipe. Seepixijs-custom-renderingskill. - Running under strict CSP? Import
'pixi.js/unsafe-eval'. See thepixijs-environmentsskill.
Quick concepts
Renderer = systems + pipes
Each renderer is composed of Systems (lifecycle services: textures, buffers, state, filters, masks) and RenderPipes (per-renderable instruction builders: sprite, graphics, mesh, particle, text, tiling). Writing a custom renderable means implementing a RenderPipe and registering it via extensions.
The render loop
app.ticker.add(fn) registers a callback that runs every frame. The TickerPlugin registers app.render() at UPDATE_PRIORITY.LOW, so ticker callbacks at NORMAL or HIGH run before the draw. Disable the plugin with autoStart: false for manual control.
Environments
DOMAdapter abstracts every DOM call PixiJS makes (canvas creation, image loading, fetch, XML parsing). Swap with DOMAdapter.set(WebWorkerAdapter) for Workers or implement a custom Adapter for Node/SSR. Must be done before Application.init.
Common Mistakes
[HIGH] Accessing app.renderer before init() resolves
Wrong:
const app = new Application();
app.init({ width: 800, height: 600 });
console.log(app.renderer.name); // undefined — init() is asyncCorrect:
const app = new Application();
await app.init({ width: 800, height: 600 });
console.log(app.renderer.name); // 'webgl' | 'webgpu' | 'canvas'Application.init() is async. app.renderer, app.canvas, and app.screen do not exist until after the promise resolves.
[HIGH] Setting DOMAdapter after Application.init
Wrong:
const app = new Application();
await app.init({ width: 800, height: 600 });
DOMAdapter.set(WebWorkerAdapter); // too late — init already allocated resourcesCorrect:
DOMAdapter.set(WebWorkerAdapter);
const app = new Application();
await app.init({ width: 800, height: 600 });The adapter abstracts DOM calls the renderer makes during construction (canvas creation, image loading, fetch). Swap it before init() or the wrong adapter is baked into the renderer.
[MEDIUM] Treating preference as a guarantee
Wrong:
await app.init({ preference: "webgpu" });
// assume WebGPU is active
useWebGPUOnlyFeature(app.renderer);Correct:
await app.init({ preference: "webgpu" });
if (app.renderer.name === "webgpu") {
useWebGPUOnlyFeature(app.renderer);
}preference is a hint, not a demand. If the browser lacks WebGPU support, PixiJS falls back to WebGL (or Canvas). Always branch on renderer.name for backend-specific code.
API Reference
Render Loop
Every PixiJS frame runs a fixed sequence of ticker callbacks in priority order, with app.render() registered at UPDATE_PRIORITY.LOW — scene graph update and GPU draw happen _inside_ that one callback. The Application's TickerPlugin drives this loop automatically. Understanding the priority order and when to render yourself is key for integrating game logic, physics, and custom frame pacing.
Frame lifecycle
Each frame, the Ticker measures elapsed time, clamps it with minFPS/maxFPS, then calls every listener registered with ticker.add() in priority order. app.render() is itself one of those listeners, registered at UPDATE_PRIORITY.LOW. When it runs, it walks the display list from app.stage, recalculates world transforms (position/rotation/scale propagated parent-to-child), fires each object's onRender hook, culls off-screen objects if culling is enabled, then batches draw calls and issues GPU commands.
requestAnimationFrame
│
[Ticker._tick()]
│
├─ Compute elapsed time (minFPS/maxFPS clamp)
└─ Call listeners in priority order
├─ INTERACTION / HIGH / NORMAL listeners
├─ LOW: app.render()
│ ├─ Traverse display list
│ ├─ Update world transforms
│ │ └─ object.onRender() (per-object during traversal)
│ ├─ Cull display objects (if enabled)
│ ├─ Upload data to GPU
│ └─ Draw
└─ UTILITY listeners (post-render)Rendering is retained mode: objects persist across frames unless you explicitly remove them.
Quick Start
app.ticker.add((ticker) => {
sprite.rotation += 0.01 * ticker.deltaTime;
});
app.ticker.add(
(ticker) => {
updatePhysics(ticker.deltaMS);
},
undefined,
UPDATE_PRIORITY.HIGH,
);Callbacks receive a Ticker instance. Use ticker.deltaTime (dimensionless, ~1.0 at 60fps) for simple multipliers; use ticker.deltaMS (milliseconds) for time-based calculations.
Core Patterns
Priority order
The ticker runs registered callbacks in descending priority. The TickerPlugin registers app.render() at UPDATE_PRIORITY.LOW, so callbacks at NORMAL, HIGH, or INTERACTION run before the render.
UPDATE_PRIORITY.INTERACTION = 50 // pointer events
UPDATE_PRIORITY.HIGH = 25 // physics, input sampling
UPDATE_PRIORITY.NORMAL = 0 // gameplay (default)
UPDATE_PRIORITY.LOW = -25 // app.render() registered here
UPDATE_PRIORITY.UTILITY = -50 // post-render cleanupimport { UPDATE_PRIORITY } from "pixi.js";
app.ticker.add(
(ticker) => {
handleInput(ticker.deltaMS);
},
undefined,
UPDATE_PRIORITY.HIGH,
);
app.ticker.add((ticker) => {
updateAnimations(ticker.deltaTime);
});Time units
| Property | Type | Scaled by speed? | Capped by minFPS? |
|---|---|---|---|
deltaTime | dimensionless (~1.0 at 60fps) | yes | yes |
deltaMS | milliseconds | yes | yes |
elapsedMS | milliseconds | no | no |
Use deltaTime as a frame-rate multiplier for simple per-frame logic; use deltaMS for pixels-per-second or other time-based math; use elapsedMS only for profiling (raw, uncapped, unscaled).
Manual rendering
await app.init({ autoStart: false, width: 800, height: 600 });
function frame(time: number) {
updateGameState();
app.renderer.render(app.stage);
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);autoStart: false disables the TickerPlugin's automatic render registration. You control when the scene draws, letting you integrate with a custom loop, an external animation library, or a fixed-timestep game clock.
Stop and start
app.stop(); // pause the ticker and rendering
app.start(); // resumeUseful for a pause menu or when the user switches away from the tab. The ticker is automatically paused on page blur when sharedTicker: false (default).
Per-object update via onRender
const sprite = new Sprite(texture);
sprite.onRender = () => {
sprite.rotation += 0.01;
};
app.stage.addChild(sprite);onRender is called during scene graph traversal, just before the object is drawn. It's a per-object hook; an alternative to a global ticker callback when the logic is tied to a specific display object.
Frame rate capping
app.ticker.maxFPS = 30; // run at 30fps
app.ticker.minFPS = 10; // cap deltaTime at 10fps worth if frames dropmaxFPS enforces a ceiling by skipping updates. minFPS caps deltaTime so large frame drops don't produce enormous deltas that break physics. Defaults: no maxFPS, minFPS: 10.
Common Mistakes
[CRITICAL] Treating the ticker callback arg as a number
Wrong:
app.ticker.add((dt) => {
sprite.rotation += dt; // dt is the Ticker instance, not a number
});Correct:
app.ticker.add((ticker) => {
sprite.rotation += ticker.deltaTime;
});v8 passes the Ticker instance to the callback, not a delta. Old v7 code that used (dt) => sprite.x += dt compiles but produces NaN because dt is an object.
[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.rotation += 0.01;
};
}
}updateTransform was removed in v8. Use onRender for per-object per-frame logic.
[MEDIUM] Assuming ticker callbacks run after render
Wrong:
app.ticker.add(() => {
readPixelsFromCanvas(); // empty; render hasn't happened yet this frame
});Correct:
app.ticker.add(
() => {
readPixelsFromCanvas();
},
undefined,
UPDATE_PRIORITY.UTILITY,
);Callbacks added at the default priority (NORMAL = 0) run _before_ the render call (at LOW = -25). Use UTILITY = -50 for post-render work like pixel readbacks or DOM sync.
API Reference
Renderers: WebGPU, WebGL, and Canvas
PixiJS v8 ships three renderers: WebGPURenderer (fastest where available), WebGLRenderer (broad compatibility, WebGL2), and CanvasRenderer (a last-resort 2D Canvas backend). autoDetectRenderer (used internally by Application.init) picks the best available backend based on a preference list.
Status: WebGLRenderer is recommended for production. WebGPURenderer is feature-complete but still experimental; browser implementations have inconsistencies. CanvasRenderer is a fallback for environments without WebGL/WebGPU and supports a reduced feature set.
Quick Start
const app = new Application();
await app.init({
preference: ["webgpu", "webgl"],
width: 800,
height: 600,
background: 0x222233,
antialias: true,
});
document.body.appendChild(app.canvas);preference can be a single string (try that first, fall back to default order) or an array (use only these, in this order; others are excluded). Default priority when preference is not set: webgl, webgpu, canvas.
Core Patterns
Preference forms
// Single string: try webgpu first, fall back through defaults
await app.init({ preference: "webgpu" });
// Array: only try listed backends in order
await app.init({ preference: ["webgpu", "webgl"] });
// Array acts as a blocklist; anything not listed is excluded
await app.init({ preference: ["webgl"] }); // never uses webgpu or canvas
// Force canvas (legacy-only environments)
await app.init({ preference: ["canvas"] });A string form falls through to defaults if the preferred backend isn't available. An array form excludes any backend not in the list, even if the preferred one fails.
Per-renderer options
await app.init({
width: 800,
height: 600,
webgpu: { antialias: true, powerPreference: "high-performance" },
webgl: { antialias: true, premultipliedAlpha: false },
canvasOptions: { backgroundAlpha: 0 },
});Common options live at the top level; renderer-specific options go in webgpu, webgl, or canvasOptions. Only the options for the selected renderer apply; the others are discarded.
Systems and pipes
Each renderer is composed of a fixed set of Systems (lifecycle services: textures, buffers, state, events, filters, masks) and a set of RenderPipes (per-renderable instruction builders: sprite, graphics, mesh, particle, text, tiling-sprite).
Systemsrun once per renderer lifecycle and manage GPU state.RenderPipesrun every frame, one per renderable type. They batch instructions for their object class and flush them to the GPU.
Writing a custom renderable means implementing a RenderPipe, a BatchableX class, and registering both via the extensions system. See pixijs-custom-rendering.
Direct renderer construction
import { WebGLRenderer } from "pixi.js";
const renderer = new WebGLRenderer();
await renderer.init({ width: 800, height: 600 });Most apps use Application.init or autoDetectRenderer(), but you can instantiate a specific renderer directly when the target environment is known (tests, tooling, editor integrations).
renderer.render() options
The .render() method accepts either a Container or an options object:
// shorthand: just render the stage
renderer.render(app.stage);
// options form: control clearing, transform, or a render target
renderer.render({
container: app.stage,
clear: true,
transform: new Matrix(),
});
// render into a specific mip level of a RenderTexture
renderer.render({
container: myContainer,
target: renderTexture,
mipLevel: 1,
});container is the scene root to draw. target is a separate destination (e.g. a RenderTexture). mipLevel > 0 is useful for custom LOD systems or manual mipmap generation.
Resizing, texture generation, and interop
renderer.resize(window.innerWidth, window.innerHeight);
const texture = renderer.generateTexture(displayObject);
// Mixing PixiJS with Three.js (or any other WebGL/WebGPU library)
threeRenderer.resetState();
threeRenderer.render(scene, camera);
pixiRenderer.resetState();
pixiRenderer.render({ container: stage });Call resetState() before each library renders. Both libraries leave GPU state (bound textures, blend modes, active shaders) that conflicts with the other; without resetState(), objects can disappear or blend incorrectly.
Destroying a renderer
renderer.destroy();Releases GPU resources, systems, pipes, and event listeners. A destroyed renderer cannot be used for further rendering.
Manual rendering
await app.init({ autoStart: false, width: 800, height: 600 });
function frame() {
app.renderer.render(app.stage);
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);Disable the automatic ticker plugin with autoStart: false, then call app.renderer.render(app.stage) yourself. Useful for integrating with an external game loop.
Renderer selection at runtime
console.log(app.renderer.name); // 'webgl', 'webgpu', or 'canvas'After init, app.renderer is the concrete class. Use app.renderer.name (a string) to branch on backend; app.renderer.type exists but is a numeric id meant for internal dispatch — prefer name in user code.
Supports check
if (await isWebGPUSupported()) {
// ok to prefer webgpu
}
if (isWebGLSupported()) {
// ok to prefer webgl
}Both helpers are exported from pixi.js. Useful if you want to tell the user which backend is about to be used before calling app.init.
Common Mistakes
[HIGH] Expecting WebGPU everywhere
Wrong:
await app.init({ preference: ["webgpu"] });Correct:
await app.init({ preference: ["webgpu", "webgl"] });WebGPU is not yet available on all browsers. An array-form preference of only ['webgpu'] will fail init on unsupported browsers (no fallback). Always include 'webgl' as a fallback unless you've verified WebGPU support upstream.
[MEDIUM] Assuming CanvasRenderer supports everything
CanvasRenderer does not support filters, masks beyond basic clipping, compressed textures, or custom shaders. It's intended as a last-resort for environments that can't run WebGL. If you need full feature parity, target WebGL or WebGPU.
[MEDIUM] Calling render before init
Wrong:
const app = new Application();
app.renderer.render(app.stage);
await app.init();Correct:
const app = new Application();
await app.init();
app.renderer.render(app.stage);app.renderer is populated only after init() resolves. Any access before that is undefined.
API Reference
Related skills
How it compares
Pick pixijs-core-concepts over animation or sprite skills when the problem is renderer backend choice or frame pipeline behavior, not asset loading or tweening.
FAQ
Who is pixijs-core-concepts for?
Frontend developers implementing PixiJS v8 apps who need renderer and loop fundamentals first.
When is WebGPU guaranteed?
Never. preference is a hint; always branch on app.renderer.name after await init().
When must DOMAdapter be set?
Before Application.init so canvas creation and fetch use the Worker or SSR adapter.
Is Pixijs Core Concepts safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.