$npx -y skills add pixijs/pixijs-skills --skill pixijs-performanceUse this skill when profiling or optimizing a PixiJS v8 app for FPS, draw calls, or GPU memory. Covers destroy patterns (cacheAsTexture(false), releaseGlobalResources), GCSystem and TextureGCSystem, PrepareSystem, object pooling, batching rules, BitmapText for dynamic text, culli
| 1 | Profile before optimizing. PixiJS handles a lot of content well out of the box; browser DevTools Performance + GPU profiling should be your first move. Once you've found the bottleneck, apply the targeted pattern below (destroy, pool, batch, cache, or cull). |
| 2 | |
| 3 | ## Quick Start |
| 4 | |
| 5 | ```ts |
| 6 | container.cacheAsTexture(true); |
| 7 | container.updateCacheTexture(); |
| 8 | container.cacheAsTexture(false); |
| 9 | container.destroy({ children: true }); |
| 10 | |
| 11 | import { CullerPlugin, extensions } from "pixi.js"; |
| 12 | extensions.add(CullerPlugin); |
| 13 | |
| 14 | offscreenContainer.cullable = true; |
| 15 | offscreenContainer.cullArea = new Rectangle(0, 0, 256, 256); |
| 16 | |
| 17 | // Tune GC via init options (ms). The `textureGC.*` properties are |
| 18 | // deprecated since 8.15.0 — use these on the Application init instead. |
| 19 | await app.init({ gcMaxUnusedTime: 60_000, gcFrequency: 30_000 }); |
| 20 | ``` |
| 21 | |
| 22 | **Related skills:** `pixijs-scene-container` (destroy options), `pixijs-scene-core-concepts` (render groups, layers, culling), `pixijs-scene-text` (BitmapText for dynamic content), `pixijs-assets` (atlasing), `pixijs-custom-rendering` (custom batchers). |
| 23 | |
| 24 | ## Core Patterns |
| 25 | |
| 26 | ### Proper destroy with cleanup |
| 27 | |
| 28 | ```ts |
| 29 | import { Sprite, Assets } from "pixi.js"; |
| 30 | |
| 31 | const texture = await Assets.load("character.png"); |
| 32 | const sprite = new Sprite(texture); |
| 33 | |
| 34 | // Destroy sprite only (preserve texture for reuse) |
| 35 | sprite.destroy(); |
| 36 | |
| 37 | // Destroy sprite AND its texture |
| 38 | sprite.destroy({ children: true, texture: true, textureSource: true }); |
| 39 | ``` |
| 40 | |
| 41 | When done with a loaded asset entirely: |
| 42 | |
| 43 | ```ts |
| 44 | Assets.unload("character.png"); |
| 45 | ``` |
| 46 | |
| 47 | This removes it from the cache and unloads the GPU resource. |
| 48 | |
| 49 | ### Application destroy/recreate cycle |
| 50 | |
| 51 | ```ts |
| 52 | import { Application } from "pixi.js"; |
| 53 | |
| 54 | // Correct destroy that cleans global pools |
| 55 | app.destroy({ releaseGlobalResources: true }); |
| 56 | |
| 57 | const newApp = new Application(); |
| 58 | await newApp.init({ width: 800, height: 600 }); |
| 59 | ``` |
| 60 | |
| 61 | Without `releaseGlobalResources: true`, pooled objects (batches, textures) from the old app leak into the new one, causing flickering and corruption. |
| 62 | |
| 63 | ### Texture garbage collection |
| 64 | |
| 65 | PixiJS auto-collects unused textures and GPU resources via `GCSystem`. Defaults: checks every 30 seconds, removes resources idle for 60 seconds. These are time-based (milliseconds). |
| 66 | |
| 67 | ```ts |
| 68 | import { Application } from "pixi.js"; |
| 69 | |
| 70 | const app = new Application(); |
| 71 | |
| 72 | await app.init({ |
| 73 | gcActive: true, |
| 74 | gcMaxUnusedTime: 120000, // idle time before cleanup in ms (default: 60000) |
| 75 | gcFrequency: 60000, // check interval in ms (default: 30000) |
| 76 | }); |
| 77 | ``` |
| 78 | |
| 79 | For manual control: |
| 80 | |
| 81 | ```ts |
| 82 | texture.source.unload(); // immediate GPU memory release |
| 83 | ``` |
| 84 | |
| 85 | ### PrepareSystem for GPU upload |
| 86 | |
| 87 | Upload textures and graphics to GPU before rendering to avoid first-frame hitches: |
| 88 | |
| 89 | ```ts |
| 90 | import "pixi.js/prepare"; |
| 91 | import { Application, Assets } from "pixi.js"; |
| 92 | |
| 93 | const app = new Application(); |
| 94 | await app.init(); |
| 95 | |
| 96 | // Don't render until assets are uploaded |
| 97 | app.stop(); |
| 98 | |
| 99 | const texture = await Assets.load("large-scene.png"); |
| 100 | |
| 101 | // Upload to GPU ahead of time |
| 102 | await app.renderer.prepare.upload(app.stage); |
| 103 | |
| 104 | // Now rendering won't hitch on first frame |
| 105 | app.start(); |
| 106 | ``` |
| 107 | |
| 108 | `prepare.upload()` accepts a Container (uploads all textures, text, and graphics in the subtree) or individual resources. |
| 109 | |
| 110 | ### cacheAsTexture for performance |
| 111 | |
| 112 | `cacheAsTexture()` renders a container's subtree to a single texture, reducing draw calls for complex static content. Internally it creates a render group and caches the result. |
| 113 | |
| 114 | **When to use:** |
| 115 | |
| 116 | - Many static children (UI panels, decorative backgrounds, complex Graphics) |
| 117 | - Containers with expensive filters (cache the filter result) |
| 118 | - Large subtrees that rarely change |
| 119 | |
| 120 | **Tradeoffs:** |
| 121 | |
| 122 | - Uses GPU memory for the cached texture (larger containers = more memory) |
| 123 | - Max texture size is GPU-dependent (typically 4096x4096; check `renderer.texture.maxTextureSize`) |
| 124 | - Must call `updateCacheTexture()` after modifying children |
| 125 | - Combining with masks is fragile (see the masking skill) |
| 126 | |
| 127 | ```ts |
| 128 | import { Container, Sprite } from "pixi.js"; |
| 129 | |
| 130 | const panel = new Container(); |
| 131 | // ... add many static children ... |
| 132 | |
| 133 | panel.cacheAsTexture(true); |
| 134 | |
| 135 | // With options |
| 136 | panel.cacheAsTexture({ resolution: 2, antialias: true }); |
| 137 | |
| 138 | // Refresh after changes |
| 139 | panel.updateCacheTexture(); |
| 140 | |
| 141 | // MUST disable before destroying (see Common Mistakes below) |
| 142 | panel.cacheAsTexture(false); |
| 143 | panel.destroy(); |
| 144 | ``` |
| 145 | |
| 146 | **Avoid:** toggling on/off repeatedly (constant re-caching negates benefits), caching sparse containers (negligible gain), caching containers lar |