PixiJS 8.21 Rendering
Set up and structure a PixiJS 8.21 application: the async Application, asset
loading via Assets, the Container/Sprite scene graph, the ticker loop,
pointer events, and render groups. Pins the 8.21 API (async init, unified
Assets, eventMode).
When to use
- Use when starting a PixiJS v8 project, fixing a blank canvas, structuring the display list, loading textures, animating via the ticker, or handling pointer input.
- Use when
package.jsondepends onpixi.js(v8) and code doesimport { Application } from 'pixi.js'.
When not to use: Phaser's scene/loader model → phaser-core. 3D scenes →
threejs-scene-setup. PixiJS v7-and-earlier code (synchronous new Application({...}), Loader, beginFill/endFill) needs the v8 migration first;
this skill targets v8 only. (interactive = true still works in v8 as an alias for
eventMode = 'static', but prefer the explicit eventMode.)
Core workflow
- Create and
awaitthe Application. In v8,new Application()is empty; configuration happens inawait app.init({...}). Appendapp.canvas(notapp.view) to the DOM. Wrap top-levelawaitin an async function for bundlers. - Load assets with
Assets.await Assets.load(url)returns aTexture. For many assets, register a manifest/bundle and load by name. There is no v7Loader. - Build the scene graph. Everything descends from
app.stage(aContainer). Group related objects inContainers; child transforms are relative to the parent. Draw order = insertion order (later = on top). - Animate with the ticker.
app.ticker.add((ticker) => {...}). Scale motion byticker.deltaTime(frames, ~1 at 60fps) orticker.deltaMS(milliseconds) so speed is frame-rate independent. - Enable events per object by setting
eventMode = 'static'(or'dynamic'), thenobj.on('pointerdown', ...). Federated pointer events cover mouse/touch/pen. - Promote big static subtrees to render groups (
isRenderGroup: true) so the GPU caches their transforms. Profile before and after; confirm pixels on screen.
Patterns
1. Async Application boot (the v8 entry point)
2. Containers for a relative-transform scene graph
3. The ticker loop (frame-rate independent)
4. Pointer events (federated)
Dragging needs globalpointermove, not pointermove. In v8 pointermove fires
only while the pointer is over the object, so a drag that follows the cursor
past the object's edge stops updating. globalpointermove fires on every move:
5. Loading many assets by name (bundles)
6. Render groups for large static layers
Pitfalls
- Blank canvas / "app.stage is undefined" → you didn't
await app.init(), or you configured the constructor. In v8 the constructor is empty; all options go toinit().app.renderer/app.canvas/app.screenareundefineduntil theinit()promise resolves. app.viewis undefined → v8 renamed it toapp.canvas.- Porting v7 code →
Loader/loader.add→Assets.load(theLoaderclass is gone); synchronousnew Application({...})→ empty constructor + asyncinit()(options in the constructor are ignored with a deprecation warning, not an error);beginFill()/endFill()→ shape-first.rect(...).fill(...).interactive = trueandapp.viewstill work as deprecated aliases. - Ticker callback arg is the Ticker, not a delta number →
app.ticker.add((dt) => { obj.rotation += dt; })compiles, butdtis the wholeTickerobject, so the math yieldsNaNand nothing animates. Readticker.deltaTimeoff the argument. - Top-level await build error (Vite ≤6.0.6) → wrap boot in
(async () => { ... })(). - Speed varies with frame rate → multiply movement by
ticker.deltaTime(~1 at 60fps) or scale byticker.deltaMS; never assume 60fps.deltaTimeis a dimensionless multiplier, not milliseconds. - Clicks do nothing → the object's
eventModeis still'passive'(the v8 default: self not interactive, children still are); set it to'static'(or'dynamic'for objects that move under a stationary cursor). - Drag stops at the object's edge →
pointermovefires only while the pointer is over the object in v8; useglobalpointermovefor drag/global tracking. Texture.from(url)returns a blank/undefined texture → in v8 it only reads the Assets cache;await Assets.load(url)first, then use the returnedTexture.- WebGPU features error out →
preferenceis a hint. If the backend is unavailable Pixi falls back (WebGL, then Canvas); branch onapp.renderer.namebefore using backend-specific code. - Textures look blurry on pixel art → set
texture.source.scaleMode = 'nearest'(or pass it when loading). - Memory grows →
removeChilddoes not free GPU memory; callsprite.destroy()andAssets.unload(url)for assets you're done with. - Flicker/corruption after tearing down and re-creating an app in the same tab →
destroy with
app.destroy({ releaseGlobalResources: true }); otherwise pooled batches/textures from the old app leak into the new one.
References
- For the texture/asset pipeline (sprite sheets/atlases,
Assets.add, background loading, unloading) and Graphics/Text/TilingSprite/ParticleContainerplus filters, readreferences/assets-and-display.md.
Related skills
phaser-core— a batteries-included 2D framework (scenes, physics, input).threejs-scene-setup— 3D in the browser with three.js.prototype-fast— greybox a playable slice quickly (often cites PixiJS).


