
Pixijs Assets
- 3.1k installs
- 293 repo stars
- Updated June 4, 2026
- pixijs/pixijs-skills
pixijs-assets is a PixiJS v8 skill for Assets loading, bundles, manifests, caching, parsers, and GPU cleanup across media types.
About
The pixijs-assets skill documents PixiJS v8's unified Assets loader, resolver, and cache for textures, video, spritesheets, fonts, JSON, GIFs, and compressed GPU formats. It recommends Assets.init for basePath, texturePreference, and manifest-driven setups, then Assets.load for URLs, aliases, arrays, or UnresolvedAsset descriptors. A parser field forces loaders for extension-less CDN or API URLs, replacing deprecated loadParser. Reference files cover bundles, manifests, background loading, progress callbacks, caching, SVG modes, resolution detection, and per-asset data options distinct from LoadOptions retry strategy. Critical v8 rules forbid Texture.from for fetching, require object-form Assets.add, and call Assets.unload between levels to free GPU memory. Supported types span png, webp, avif, mp4, fnt, ktx2, basis, and gif with optional side-effect imports. Decision guidance maps single images, level bundles, loading bars, and memory budgets to the right reference workflow.
- Assets.init, load, get, unload, and bundle patterns for PixiJS v8.
- Parser field for extension-less URLs with full supported type table.
- Separate LoadOptions retries versus per-asset data parser options.
- Reference index for bundles, manifests, background load, and progress bars.
- Critical fixes for Texture.from misuse and positional Assets.add removal.
Pixijs Assets by the numbers
- 3,057 all-time installs (skills.sh)
- +221 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #160 of 2,277 Frontend Development skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
pixijs-assets capabilities & compatibility
- Capabilities
- assets.init configuration for basepath and manif · multi format loading with parser override suppor · bundle and backgroundload level transition patte · loadoptions retry, skip, and onprogress callback · cache lookup via assets.get and unload cleanup · reference routing for video, fonts, svg, and ktx
- Use cases
- frontend · ui design
- Platforms
- macOS · Windows · Linux
- IDEs
- vscode · cursor ide · webstorm
- Pricing
- Free
What pixijs-assets says it does
In v8, `Texture.from()` only reads the cache. It does not fetch from a URL.
The positional `Assets.add(key, url)` form was removed in v8.
For level-based games or screens with distinct asset sets, call `Assets.unloadBundle()` when transitioning
npx skills add https://github.com/pixijs/pixijs-skills --skill pixijs-assetsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 3.1k |
|---|---|
| repo stars | ★ 293 |
| Security audit | 2 / 3 scanners passed |
| Last updated | June 4, 2026 |
| Repository | pixijs/pixijs-skills ↗ |
How do I load and manage PixiJS v8 textures, fonts, video, and spritesheets with bundles, progress, and proper cache cleanup?
Load, bundle, cache, and unload PixiJS v8 textures, fonts, video, spritesheets, and compressed assets with Assets API.
Who is it for?
PixiJS v8 game or web canvas projects needing authoritative asset loader guidance and reference routing.
Skip if: Skip for PixiJS v7 legacy loadParser-only codebases or pure scene graph work without asset loading.
When should I use this skill?
User mentions Assets.load, bundles, manifests, spritesheets, video textures, parser, or PixiJS v8 resource loading.
What you get
Correct Assets.load usage, bundle or manifest setup, progress handling, and unload patterns that avoid v8 cache and GPU leaks.
- Background loading implementation
- Staged bundle preload flow
- Non-blocking screen transition pattern
Files
The Assets API is PixiJS's asset loader, resolver, and cache in one singleton. Use it to load textures, video, spritesheets, fonts, JSON, and other resources with format detection, resolution switching, bundle grouping, progress tracking, and GPU cleanup.
Quick Start
await Assets.init({ basePath: "/static/" });
const texture = await Assets.load("bunny.png");
const sprite = new Sprite(texture);
app.stage.addChild(sprite);
const [hero, enemy] = await Assets.load(["hero.png", "enemy.png"]);
await Assets.load({
alias: "logo",
src: "logo.webp",
});
const logo = new Sprite(Assets.get("logo"));Assets.init() is optional but recommended for setting basePath, texturePreference, or a manifest. After init, call Assets.load() with a URL, alias, array, or UnresolvedAsset; resolved assets are cached and re-resolved by Assets.get().
Supported file types
| Type | Extensions | Parser ID | Loader |
|---|---|---|---|
| Textures | .png, .jpg, .jpeg, .webp, .avif | texture | loadTextures |
| SVG | .svg | svg | loadSvg (see references/svg.md) |
| Video textures | .mp4, .m4v, .webm, .ogg, .ogv, .h264, .avi, .mov | video | loadVideoTextures (see references/video.md) |
| Sprite sheets | .json (Spritesheet format) | spritesheet | spritesheetAsset (see references/spritesheet.md) |
| Bitmap fonts | .fnt, .xml | bitmap-font | loadBitmapFont (loading works by default; rendering BitmapText requires 'pixi.js/text-bitmap'; see references/fonts.md) |
| Web fonts | .ttf, .otf, .woff, .woff2 | web-font | loadWebFont (see references/fonts.md) |
| JSON | .json | json | loadJson |
| Text | .txt | text | loadTxt |
| Compressed textures | .basis, .dds, .ktx, .ktx2 | basis, dds, ktx, ktx2 | See references/compressed-textures.md |
| Animated GIFs | .gif | gif | Requires 'pixi.js/gif'; returns GifSource (see references/gif.md) |
The Parser ID column is the value you pass to the top-level parser field on an asset descriptor to force a specific loader. See "Forcing a parser" below.
Forcing a parser with parser
By default, PixiJS picks a loader by matching the file extension or MIME type. When your URL lacks an extension (CDN signed URLs, blob URLs, API endpoints, content-hashed paths), the resolver can't tell the loader what to do. Set the top-level parser field on the asset descriptor to force a specific loader:
// Signed CDN URL with no extension
const texture = await Assets.load({
src: "https://cdn.example.com/signed/abc123?token=xyz",
parser: "texture",
});
// API endpoint that returns JSON
const data = await Assets.load({
alias: "config",
src: "https://api.example.com/v1/config",
parser: "json",
});
// Extension-less font URL with explicit family
await Assets.load({
src: "https://cdn.example.com/fonts/hero-v2",
parser: "web-font",
data: { family: "Hero", weights: ["400", "700"] },
});
// Video stream without a file extension
const clipTexture = await Assets.load({
src: "https://cdn.example.com/stream/xyz",
parser: "video",
data: { mime: "video/mp4", muted: true, playsinline: true },
});The parser field goes at the top level of the asset descriptor (alongside src and data), not inside data. It takes any parser ID from the "Supported file types" table above:
'texture','svg','video': image, SVG, and video textures'json','text': JSON and plain text'web-font','bitmap-font': web and bitmap fonts'spritesheet': texture atlas JSON'gif': animated GIFs (requires'pixi.js/gif')'basis','dds','ktx','ktx2': compressed textures (each requires its side-effect import)
When you need it
- Signed CDN URLs:
https://cdn.example.com/get?id=abc123has no extension the loader can test against. - Blob or ObjectURL:
URL.createObjectURL(blob)producesblob:...URLs with no extension. - Custom routing:
/api/assets/hero-v2where the server decides the content type. - Content-hashed paths without suffix: some build pipelines produce names like
/static/abc123definstead of/static/abc123def.png.
If the URL _does_ have an extension, you don't need parser; let auto-detection do its job. Only set parser when detection can't work.
loadParser is deprecated
The v7 loadParser field still works but emits a deprecation warning. Use parser for new code.
// Old (deprecated)
await Assets.load({ src: "...", loadParser: "loadTextures" });
// New
await Assets.load({ src: "...", parser: "texture" });Topics
Every asset workflow is covered in a reference file. Pick the one that matches the question:
| Topic | Reference | When |
|---|---|---|
| Texture atlases and animations | references/spritesheet.md | Loading sprite sheets with AnimatedSprite |
| Video textures | references/video.md | .mp4, .webm, autoplay, looping, mobile |
| Web and bitmap fonts | references/fonts.md | .woff2, .fnt, font families, SDF fonts |
| Animated GIFs | references/gif.md | .gif, GifSprite, playback control |
| Grouping assets by feature | references/bundles.md | addBundle, loadBundle, unloadBundle |
| Declaring everything upfront | references/manifests.md | Assets.init({ manifest }) workflows |
| Cache lookups and cleanup | references/caching.md | Assets.get, Assets.unload, Cache |
| Priming future assets | references/background.md | backgroundLoad, backgroundLoadBundle |
| Loading screens | references/progress.md | onProgress, LoadOptions progress |
| GPU-compressed formats | references/compressed-textures.md | .ktx2, .basis, .dds, .ktx |
| Vector vs raster SVG | references/svg.md | parseAsGraphicsContext, texture mode |
| Retina + format detection | references/resolution.md | @{1,2}x, format preferences |
Decision guide
- Need to load a single image? Use
Assets.load(url). No setup required. - Loading many assets grouped by level/scene? Use a bundle. See
references/bundles.md. - Know all assets at build time? Use a manifest in
Assets.init. Seereferences/manifests.md. - Need a loading bar? Pass a progress callback to
Assets.load. Seereferences/progress.md. - Smooth transitions between levels? Background-load the next level. See
references/background.md. - Memory budget matters? Use compressed textures and
Assets.unloadbetween screens. Seereferences/compressed-textures.mdandreferences/caching.md. - Need crisp SVG icons at any size? Load as Graphics, not texture. See
references/svg.md. - Retina + WebP/AVIF? Configure
texturePreferenceand use format patterns. Seereferences/resolution.md.
Load options and error handling
There are two separate "options" concepts when loading assets:
1. `LoadOptions`: the second argument to Assets.load/loadBundle. Controls error recovery, retries, progress, and completion callbacks across a whole load. 2. `data`: a field on each asset descriptor. Forwards parser-specific options (scale mode, resolution, font family, autoplay flags, etc.) to the specific loader for that asset.
LoadOptions (per call)
await Assets.load(["hero.png", "enemy.png"], {
onProgress: (p) => updateBar(p),
onError: (err, url) => {
const src = typeof url === "string" ? url : url.src;
console.warn("failed:", src, err);
},
strategy: "retry",
retryCount: 3,
retryDelay: 250,
});onProgress(progress):[0, 1]as assets in the call complete.onError(error, url):urlisstring | ResolvedAsset. Guard before reading.src; whenurlis a string,.srcis undefined.strategy: 'throw' | 'skip' | 'retry'— default'throw'.'skip'resolves with any successful assets;'retry'reattempts the failed ones.retryCount— default3, retries per asset whenstrategyis'retry'.retryDelay— default250ms between retries.
Global defaults live on Loader.defaultOptions, or pass loadOptions to Assets.init().
data options (per asset)
Each loader parser reads its own options from the data field on the asset descriptor. Use the table below to pick the right options for each asset type:
| Asset type | data shape | Key options | Reference |
|---|---|---|---|
| Texture (image) | TextureSourceOptions | resolution, scaleMode, alphaMode, autoGenerateMipmaps, antialias, addressMode | references/resolution.md |
| SVG | { parseAsGraphicsContext?, resolution? } | parseAsGraphicsContext for Graphics mode; resolution for sharper raster | references/svg.md |
| Video | VideoSourceOptions | autoPlay, loop, muted, playsinline, preload, updateFPS, crossorigin, mime | references/video.md |
| Web font | LoadFontData | family, weights, style, display, unicodeRange, featureSettings | references/fonts.md |
| Bitmap font | (none; auto-configured) | Distance-field detection sets scale mode and mipmaps | references/fonts.md |
| Spritesheet | { texture?, imageFilename?, ignoreMultiPack?, textureOptions?, cachePrefix? } | textureOptions forwards TextureSourceOptions (e.g. scaleMode) to the atlas image; texture to skip image load; imageFilename to override the referenced image; ignoreMultiPack to skip multi-pack follow-ups; cachePrefix to namespace frames | references/spritesheet.md |
| GIF | GifBufferOptions | fps, scaleMode, resolution, autoGenerateMipmaps | references/gif.md |
| Compressed texture | TextureSourceOptions | scaleMode, addressMode, autoGenerateMipmaps | references/compressed-textures.md |
| JSON / Text | (none) | Returned as-is | — |
Example combining LoadOptions and data:
await Assets.load(
{
alias: "hero",
src: "hero.png",
data: { scaleMode: "nearest", resolution: 2 },
},
{ strategy: "retry", retryCount: 3 },
);Inside a manifest or bundle, every entry can carry its own data:
await Assets.init({
manifest: {
bundles: [
{
name: "level1",
assets: [
{ alias: "tiles", src: "tiles.png", data: { scaleMode: "nearest" } },
{ alias: "font", src: "hero.woff2", data: { family: "Hero" } },
{
alias: "clip",
src: "intro.mp4",
data: { autoPlay: false, muted: true },
},
],
},
],
},
});Runtime configuration
Assets.init(options) accepts, alongside basePath and manifest:
defaultSearchParams— string orRecord<string, any>appended to every resolved URL. Useful for cache busting.skipDetections: boolean— bypass browser format detection for faster init. Requires explicittexturePreference.format.bundleIdentifier: BundleIdentifierOptions— customize how bundle keys resolve so the same alias can live in multiple bundles.loadOptions: Partial<LoadOptions>— set the defaultstrategy,retryCount,retryDelay, and callbacks for every subsequentAssets.loadcall.preferences: Partial<AssetsPreferences>—crossOrigin,preferWorkers,preferCreateImageBitmap,parseAsGraphicsContext.
After init, preferences can still be tuned:
Assets.setPreferences({
crossOrigin: "anonymous",
preferCreateImageBitmap: false,
});
for (const detection of Assets.detections) {
console.log(detection.extension);
}
Assets.reset();Assets.setPreferences(preferences)— push new preferences to every parser that supports them.Assets.detections— getter exposing the registeredFormatDetectionParserlist; use when inspecting what formats the current environment advertises.Assets.reset()— internal full reset (resolver + loader + cache). Intended for tests so a freshAssets.initcan run.
Common Mistakes
[CRITICAL] Using Texture.from(url) to load
Wrong:
const texture = Texture.from("https://example.com/image.png");Correct:
const texture = await Assets.load("https://example.com/image.png");In v8, Texture.from() only reads the cache. It does not fetch from a URL. Use Assets.load() first; the return value is the texture itself.
[HIGH] Using positional Assets.add signature
Wrong:
Assets.add("bunny", "bunny.png");Correct:
Assets.add({ alias: "bunny", src: "bunny.png" });The positional Assets.add(key, url) form was removed in v8. Use the options object with alias and src properties.
[HIGH] Not unloading textures between levels
Assets.load() caches textures indefinitely. For level-based games or screens with distinct asset sets, call Assets.unloadBundle() when transitioning to release GPU memory.
API Reference
Background Loading
Background loading lets PixiJS fetch and prepare assets passively while other work happens. Use it to prime the next level while the current one is playing, preload assets during a splash screen, or prepare UI assets during initial load. backgroundLoad and backgroundLoadBundle are non-blocking and return immediately.
Quick Start
await Assets.loadBundle("menu");
showMenu();
Assets.backgroundLoadBundle("level1");
playerClicksStart(() => {
Assets.loadBundle("level1").then(() => startLevel());
});The backgroundLoadBundle call starts loading level1 immediately without blocking. The later loadBundle call resolves quickly if background loading finished in the meantime.
Core Patterns
Background-loading a single asset
Assets.backgroundLoad("images/level2-assets.png");
// later, when we need it:
const texture = await Assets.load("images/level2-assets.png");Fire-and-forget. Background loading happens one asset at a time to avoid blocking the main thread. When your code later calls Assets.load(url) for the same asset, it either resolves immediately (if background work finished) or waits for the already-in-progress load.
Background-loading an array
Assets.backgroundLoad([
"images/sprite1.png",
"images/sprite2.png",
"images/background.png",
]);Queues multiple assets for background loading. They're processed sequentially.
Background-loading a bundle
await Assets.init({
manifest: {
bundles: [
{ name: "home", assets: [{ alias: "bg", src: "home-bg.png" }] },
{ name: "level-1", assets: [{ alias: "map", src: "level1-map.json" }] },
],
},
});
await Assets.loadBundle("home");
showHome();
Assets.backgroundLoadBundle("level-1");
onPlayClicked(async () => {
await Assets.loadBundle("level-1");
startLevel();
});Same idea for bundles. Bundle assets are queued one at a time. Requires the bundle to exist via the manifest or addBundle.
Interrupting background loading
Assets.backgroundLoadBundle("level-2");
onPlayerDecidedLevel3(async () => {
await Assets.loadBundle("level-3");
});Calling Assets.load() or Assets.loadBundle() interrupts background loading safely. The current background asset finishes, then the explicit load takes priority. Your explicit load resolves normally.
Combining with a progress bar
Background loading has no progress callback; it runs silently. To show a loading bar, use Assets.loadBundle(name, onProgress) in the foreground instead. Typical pattern: use background loading for nice-to-have preloads and foreground loading for the bundles you're actually waiting on.
Common Mistakes
[HIGH] Expecting progress from background loading
Wrong:
Assets.backgroundLoadBundle("level2", (p) => updateBar(p));Correct:
Assets.backgroundLoadBundle("level2");
// show a spinner or no UI while it runsbackgroundLoad and backgroundLoadBundle don't accept progress callbacks. They're silent. For visible progress, use the foreground Assets.load / Assets.loadBundle with an onProgress argument.
[MEDIUM] Assuming background load completes before next foreground load
Wrong:
Assets.backgroundLoadBundle("level-2");
setTimeout(() => startLevel2(), 0);Correct:
Assets.backgroundLoadBundle("level-2");
onStart(async () => {
await Assets.loadBundle("level-2");
startLevel2();
});Background loading is best-effort. If the player hits Start before it finishes, the foreground loadBundle still needs to await the remaining work. Always await the real load before using the assets.
[MEDIUM] Background-loading assets that are never used
Each queued background load consumes bandwidth and GPU memory. If the player never visits level 2, you've wasted that bandwidth. Background-load only what you're confident will be needed soon.
API Reference
Bundles
A bundle is a named group of assets you can load or unload as a unit. Use bundles to batch assets per game level, UI screen, or feature so that a single loadBundle(name) call resolves everything needed for that context. Bundles play well with background loading and manifest-based workflows.
Quick Start
Assets.addBundle("level1", [
{ alias: "bg", src: "level1/background.png" },
{ alias: "tileset", src: "level1/tiles.json" },
{ alias: "theme", src: "level1/music.mp3" },
]);
const resources = await Assets.loadBundle("level1");
const bg = Sprite.from("bg");loadBundle(name) returns a Record<alias, asset>. Each alias is also available via Assets.get(alias) after the bundle resolves.
Core Patterns
Programmatic bundles
Assets.addBundle("ui", [
{ alias: "button", src: "ui/button.png" },
{ alias: "panel", src: "ui/panel.png" },
{ alias: "cursor", src: "ui/cursor.png" },
]);
await Assets.loadBundle("ui");addBundle(name, assets) registers a bundle at runtime. The assets array uses the same { alias, src, data? } shape as Assets.add().
Loading multiple bundles at once
await Assets.loadBundle(["ui", "level1", "sounds"]);Pass an array of bundle names to load them in parallel. The returned object is keyed by bundle name: { ui: {...}, level1: {...}, sounds: {...} }.
Progress across a bundle
await Assets.loadBundle("level1", (progress) => {
loadingBar.width = progress * maxBarWidth;
});The second argument is a ProgressCallback that fires as each asset in the bundle completes. Progress is normalized [0, 1] across the entire bundle.
Unloading a bundle
await Assets.unloadBundle("level1");Tears down every asset in the bundle, releases GPU memory, and removes cached entries. Remove any Sprites or Text that reference the bundle's textures before unloading.
Sharing assets between bundles
Assets.addBundle("main", [{ alias: "hero", src: "hero.png" }]);
Assets.addBundle("bossArea", [{ alias: "hero", src: "hero.png" }]);If two bundles declare the same alias with the same src, the underlying texture is loaded once and shared. Unloading one bundle does not evict the shared asset if the other bundle still references it.
Bundle IDs
const resources = await Assets.loadBundle("level1");
const bg = resources["bg"];
const fromCache = Assets.get("bg");
const fromNamespaced = Assets.get("level1-bg");Inside the resolver, bundle assets are stored under a combined key like 'level1-bg' by default. The plain 'bg' alias still resolves thanks to resolver shortcuts. If you need to override the format, pass bundleIdentifier to Assets.init().
Common Mistakes
[HIGH] Unloading while assets are still in use
Wrong:
await Assets.unloadBundle("level1");
// level1 sprites still on the stage; they now reference destroyed texturesCorrect:
level1Container.destroy({ children: true });
await Assets.unloadBundle("level1");Always destroy (or detach from the scene) any display objects that reference the bundle's textures before unloading. Otherwise the renderer hits freed GPU memory and errors.
[MEDIUM] Confusing addBundle and init manifests
Wrong:
await Assets.init();
Assets.addBundle('ui', [...]);
await Assets.loadBundle('ui');Both work; this is not actually wrong; but a manifest passed to Assets.init({ manifest: {...} }) registers all bundles at init time in one place. Prefer manifests when you know all bundles upfront; prefer addBundle when bundles are discovered dynamically at runtime.
API Reference
Caching
Every asset loaded through Assets is cached until you explicitly unload it. The cache is a global singleton keyed by the resolved URL, alias, and any bundle identifier. Use the cache to avoid double-loading, retrieve loaded assets synchronously, and reclaim GPU memory when an asset is no longer needed.
Quick Start
await Assets.load("hero.png");
const cached = Assets.get("hero.png");
const sprite = new Sprite(cached);
await Assets.unload("hero.png");Assets.load adds the asset to the cache; Assets.get retrieves it synchronously; Assets.unload removes it and releases the underlying GPU resource.
Core Patterns
Synchronous access to already-loaded assets
await Assets.load(["hero.png", "enemy.png"]);
const hero = Assets.get("hero");
const enemy = Assets.get("enemy");
if (hero) {
app.stage.addChild(new Sprite(hero));
}Assets.get(alias) returns undefined if the asset has not been loaded yet. It is synchronous; use it in tight loops or anywhere await would be awkward.
Multiple keys at once
await Assets.load(["hero", "enemy", "boss"]);
const all = Assets.get(["hero", "enemy", "boss"]);
const heroTex = all["hero"];Passing an array returns an object keyed by alias. Useful for retrieving all the assets a scene needs in one call.
Deduping load calls
const tex1 = await Assets.load("hero.png");
const tex2 = await Assets.load("hero.png");
console.log(tex1 === tex2); // trueCalling Assets.load with the same key multiple times returns the same cached result immediately on subsequent calls. Safe to call from multiple async functions concurrently.
Unloading
sprite.destroy();
await Assets.unload("hero.png");Assets.unload(id) removes the asset from the cache, calls destroy() on the texture (releasing GPU memory), and removes any loader-specific wrappers. Always destroy or detach display objects that reference the asset before unloading, or you'll hit freed GPU memory.
Unloading a bundle
level1Container.destroy({ children: true });
await Assets.unloadBundle("level1");Prefer unloadBundle over manually iterating. It releases every asset in the bundle and respects reference counting; if another bundle also uses one of the assets, it stays in the cache.
Inspecting the cache
import { Cache } from "pixi.js";
const key = "hero.png";
if (Cache.has(key)) {
const texture = Cache.get(key);
}The low-level Cache class backs Assets.get. It exposes has(key), get(key), set(key, value), and remove(key). Most code should go through Assets.get/unload instead, but direct Cache access is useful for diagnostics or custom loader plugins.
Common Mistakes
[CRITICAL] Using Assets.get before Assets.load
Wrong:
const texture = Assets.get("hero.png");
const sprite = new Sprite(texture); // texture is undefinedCorrect:
await Assets.load("hero.png");
const texture = Assets.get("hero.png");
const sprite = new Sprite(texture);Assets.get is synchronous and returns undefined if the asset hasn't been loaded yet. Always await Assets.load first, or use the return value of Assets.load directly.
[HIGH] Not unloading between levels
Textures stay in the cache indefinitely. A game that loads level after level without calling Assets.unload or Assets.unloadBundle slowly accumulates GPU memory until it hits browser limits.
await Assets.unloadBundle("level1");
await Assets.loadBundle("level2");[MEDIUM] Using sprites after unload
Wrong:
const sprite = new Sprite(texture);
await Assets.unload("hero.png");
// sprite still references the destroyed textureCorrect:
sprite.destroy();
await Assets.unload("hero.png");Assets.unload destroys the underlying texture. Any sprite still referencing it will render Texture.EMPTY or crash depending on the backend. Destroy the leaf (or reassign its texture) before unloading.
API Reference
Compressed Textures
GPU-compressed texture formats (DDS, KTX, KTX2, Basis) use 4–8x less GPU memory than PNG/JPEG and skip runtime decoding. Use them for large textures, high-asset-count games, and anywhere memory budget matters. Each format requires a side-effect import to register its loader.
Quick Start
import "pixi.js/ktx2";
import { Assets, Sprite } from "pixi.js";
const texture = await Assets.load("background.ktx2");
const bg = new Sprite(texture);
app.stage.addChild(bg);Without the side-effect import, the loader won't know how to parse the file and will skip it silently.
Core Patterns
Format imports
import "pixi.js/dds"; // DirectDraw Surface (.dds)
import "pixi.js/ktx"; // KTX (Khronos Texture) v1
import "pixi.js/ktx2"; // KTX v2 (with Basis Universal support)
import "pixi.js/basis"; // Basis Universal (.basis)Each import registers a LoaderParser that can decode the matching file extension. Import only the formats you use; the loaders include decoder runtime that adds to your bundle size.
Format selection matrix
| Format | Platforms | Notes |
|---|---|---|
| DDS | All | Legacy desktop formats (DXT, BC). Fast decoder; wide support. |
| KTX | All | Older Khronos container. Prefer KTX2 for new projects. |
| KTX2 | All | Newer container; typically used with Basis Universal. GPU-native when available. |
| Basis | All | Supercompressed; transcodes to the GPU-native format at load time. Best for "one file, any platform" workflows. |
Use KTX2 + Basis for new projects unless you have a specific need for one of the others.
With the resolver
import "pixi.js/ktx2";
import { Assets } from "pixi.js";
Assets.add({
alias: "background",
src: "background.{ktx2,webp,png}",
});
const texture = await Assets.load("background");List compressed textures as first-preference format in the resolver. On browsers or hardware that can't sample the compressed format, the resolver falls back to WebP or PNG automatically.
Async decoding
import "pixi.js/basis";
import { Assets } from "pixi.js";
const texture = await Assets.load("hero.basis");Basis Universal decodes asynchronously on a worker thread (where supported). You don't need to await anything beyond the normal Assets.load; the loader handles decoding.
Multiple compressed formats at once
import "pixi.js/ktx2";
import "pixi.js/basis";
import "pixi.js/dds";Multiple format imports can coexist. Each registers an extension for its own file type. If you only use one format, import only that one to keep the bundle smaller.
Common Mistakes
[CRITICAL] Missing the format import
Wrong:
import { Assets } from "pixi.js";
await Assets.load("background.ktx2");Correct:
import "pixi.js/ktx2";
import { Assets } from "pixi.js";
await Assets.load("background.ktx2");Without the side-effect import, no loader parser is registered for .ktx2 files. The load silently does nothing (or errors) and you get undefined back.
[HIGH] Relying on mipmaps from compressed textures in Canvas2D
Compressed textures require WebGL or WebGPU. The Canvas2D backend can't sample them. If you need Canvas support, include a PNG/WebP fallback via the resolver format list:
Assets.add({ alias: "bg", src: "bg.{ktx2,png}" });[MEDIUM] Large Basis files in WebGL1
Basis Universal transcodes to different GPU formats based on what the device supports. On WebGL1 without extensions, some devices fall back to an uncompressed format, defeating the memory savings. Check renderer.context.supports for supported compressed formats if you're targeting older hardware.
API Reference
Fonts
PixiJS loads two kinds of fonts through Assets: web fonts (TTF/OTF/WOFF/WOFF2) via loadWebFont, and bitmap fonts (FNT/XML) via loadBitmapFont. Use web fonts for Text and HTMLText; use bitmap fonts for BitmapText when you need GPU-friendly rendering without runtime text layout.
Web fonts
Quick Start
await Assets.load("fonts/titan-one.woff2");
const text = new Text({
text: "Hello world",
style: { fontFamily: "Titan One", fontSize: 48 },
});Once the font is loaded, reference it by its derived family name in any TextStyle. PixiJS registers the font with the browser's FontFaceSet, so DOM elements and Canvas can use it too.
Family name derivation
// Loaded URL → Derived family name
// fonts/titan-one.woff → 'Titan One'
// fonts/open_sans.ttf → 'Open Sans'
// fonts/my-custom-font.otf → 'My Custom Font'By default, the family name is the filename without extension, with dashes and underscores replaced by spaces and title-cased. Override it with data.family.
Load options via data
await Assets.load({
alias: "hero-font",
src: "fonts/hero.woff2",
data: {
family: "HeroFont",
weights: ["normal", "bold"],
style: "italic",
display: "swap",
unicodeRange: "U+0000-00FF",
stretch: "expanded",
featureSettings: '"liga" 1',
},
});Fields on data map directly to FontFace descriptors:
| Option | Purpose |
|---|---|
family | Override the derived font family name |
weights | Array of weights to register. One FontFace per weight, all sharing the same URL. Valid values: 'normal', 'bold', '100'–'900' |
style | 'normal', 'italic', 'oblique' |
display | CSS font-display ('auto', 'block', 'swap', 'fallback', 'optional') |
unicodeRange | Restrict which codepoints the font covers |
stretch | 'normal', 'condensed', 'expanded', etc. |
variant | CSS font-variant value |
featureSettings | OpenType feature settings like '"liga" 1, "dlig" 1' |
Loading multiple weights
await Assets.load({
src: "fonts/inter.woff2",
data: {
family: "Inter",
weights: ["400", "700"],
},
});A single URL registers as multiple weights. Use when your font file contains a variable font or when your platform lets one file satisfy multiple weights.
Supported extensions
.ttf, .otf, .woff, .woff2. WOFF2 is the smallest and universally supported in current browsers. Prefer it unless you're targeting very old environments.
Bitmap fonts
Quick Start
import "pixi.js/text-bitmap";
await Assets.load("fonts/arial.fnt");
const text = new BitmapText({
text: "Score: 9999",
style: { fontFamily: "Arial", fontSize: 32 },
});The side-effect import registers the CanvasBitmapTextPipe and BitmapTextPipe rendering pipes. Assets.load('font.fnt') works without it and returns a BitmapFont, but rendering a BitmapText fails at render time without the import.
Supported formats
| Extension | Format |
|---|---|
.fnt | BMFont text or XML |
.xml | BMFont XML |
The parser sniffs the content to pick between text and XML automatically.
Texture page loading
// my-font.fnt references my-font_0.png, my-font_1.png
await Assets.load("fonts/my-font.fnt");Bitmap fonts reference page images by filename in the .fnt data. The parser resolves these relative to the .fnt URL and loads them automatically. Any search params on the .fnt URL (e.g. cache busting) propagate to the page texture URLs.
Signed Distance Field (SDF) fonts
The parser detects distance field metadata in the .fnt file and enables linear scale mode plus disables mipmaps automatically. No extra configuration needed from the caller. SDF fonts stay crisp at any size, so you can render a single bitmap font at many different sizes.
Accessing the BitmapFont instance
await Assets.load({ alias: "arial", src: "fonts/arial.fnt" });
const font = Assets.get("arial");
console.log(font.chars);
console.log(font.fontFamily);The cached asset is a BitmapFont instance. Assets.get('arial') and Assets.get('arial-bitmap') both return it, and Assets.get('Arial-bitmap') works if fontFamily matches.
Forcing the font parser
If your font URL lacks an extension, force the loader:
await Assets.load({
src: "https://cdn.example.com/fonts/abc123",
parser: "web-font",
data: { family: "Hero", weights: ["400", "700"] },
});
await Assets.load({
src: "https://cdn.example.com/fonts/hero-bmfont",
parser: "bitmap-font",
});See the main SKILL.md section on "Forcing a parser with parser" for the full list of parser IDs.
Common Mistakes
[HIGH] Forgetting the bitmap-font import
Wrong:
import { Assets, BitmapText } from "pixi.js";
await Assets.load("arial.fnt");
const text = new BitmapText({ text: "Hi", style: { fontFamily: "Arial" } });
app.stage.addChild(text);Correct:
import "pixi.js/text-bitmap";
import { Assets, BitmapText } from "pixi.js";
await Assets.load("arial.fnt");
const text = new BitmapText({ text: "Hi", style: { fontFamily: "Arial" } });
app.stage.addChild(text);Assets.load('arial.fnt') succeeds in the default bundle and returns a BitmapFont, but 'pixi.js/text-bitmap' registers the CanvasBitmapTextPipe and BitmapTextPipe. Without it, BitmapText renders nothing or errors at render time.
[HIGH] Using Text before the font is loaded
Wrong:
const text = new Text({ text: "Hi", style: { fontFamily: "Hero" } });
Assets.load("fonts/hero.woff2");Correct:
await Assets.load("fonts/hero.woff2");
const text = new Text({ text: "Hi", style: { fontFamily: "Hero" } });The browser falls back to a system font if the named family isn't registered yet, then the Text is cached at that fallback style. Reloading the font doesn't repaint existing Text objects.
[MEDIUM] Family name mismatch
If you don't pass data.family, the family name is derived from the filename. my_hero_font.woff becomes 'My Hero Font'; use exactly that string in your TextStyle.fontFamily, or set data.family to an explicit value.
API Reference
Animated GIFs
PixiJS loads animated GIFs through Assets.load() and returns a GifSource containing all decoded frames. Pass the source to GifSprite for playback, looping, and frame control. GIF support is opt-in via the 'pixi.js/gif' side-effect import.
Quick Start
import "pixi.js/gif";
import { Assets, GifSprite } from "pixi.js";
const source = await Assets.load("explosion.gif");
const gif = new GifSprite(source);
app.stage.addChild(gif);Without 'pixi.js/gif', the .gif loader parser isn't registered and Assets.load() won't know what to do with the file.
Core Patterns
Load options via data
const source = await Assets.load({
src: "pixel-art.gif",
data: {
fps: 12,
scaleMode: "nearest",
resolution: 2,
autoGenerateMipmaps: false,
},
});data accepts GifBufferOptions, which extends CanvasSourceOptions:
| Option | Default | Purpose |
|---|---|---|
fps | 30 | Fallback frame rate when the GIF has no per-frame delay metadata |
scaleMode | 'linear' | 'nearest' for pixel art, 'linear' for smooth scaling |
resolution | 1 | Render resolution of the decoded frames |
autoGenerateMipmaps | false | Generate mipmaps for downscaling |
Any other CanvasSourceOptions field works here too.
GifSprite options
const gif = new GifSprite({
source,
autoPlay: true,
loop: true,
animationSpeed: 0.5,
autoUpdate: true,
fps: 30,
onComplete: () => console.log("done"),
onLoop: () => console.log("looped"),
onFrameChange: (frame) => console.log(frame),
});Construction options control playback behavior; they're independent of the load-time data options.
Loading from a data URI
const source = await Assets.load(
"data:image/gif;base64,R0lGODlhAQABAAAAACw...",
);The parser matches both .gif file extensions and data:image/gif URIs. No extra configuration needed.
Playback control
gif.play();
gif.stop();
gif.currentFrame = 5;
gif.animationSpeed = 0.5;
gif.loop = false;
console.log(gif.totalFrames);
console.log(gif.playing);
console.log(gif.progress);
console.log(gif.duration);Sharing a source across sprites
const source = await Assets.load("spin.gif");
const a = new GifSprite(source);
const b = new GifSprite(source);
a.animationSpeed = 1;
b.animationSpeed = 0.25;Multiple GifSprite instances can share the same GifSource; each gets independent playback state. The underlying textures are shared, so this is cheap.
Cleanup
gif.destroy(); // destroys the sprite, not the source
gif.destroy(true); // destroys the sprite AND the source (breaks other sprites using it)
await Assets.unload("explosion.gif"); // unload through the Assets systemPrefer Assets.unload unless you know no other sprite shares the same GifSource.
Forcing the GIF parser
If your GIF URL lacks an extension, force the parser:
const source = await Assets.load({
src: "https://cdn.example.com/gif/abc123",
parser: "gif",
data: { fps: 24 },
});See the main SKILL.md section on "Forcing a parser with parser" for the full list of parser IDs.
Common Mistakes
[CRITICAL] Missing the GIF import
Wrong:
import { Assets } from "pixi.js";
await Assets.load("explosion.gif");Correct:
import "pixi.js/gif";
import { Assets } from "pixi.js";
await Assets.load("explosion.gif");Without 'pixi.js/gif', the loader parser isn't registered. The call fails or returns undefined.
[MEDIUM] Using GIF when a spritesheet would be cheaper
GIF decoding creates one texture per frame. For a 30-frame animation that's 30 separate uploads. A spritesheet packs all frames into a single atlas texture, which batches better and uses less GPU memory. Prefer spritesheets for performance-critical game animations; use GIFs for convenience and one-off effects.
[MEDIUM] destroy(true) when other sprites share the source
const source = await Assets.load("spin.gif");
const a = new GifSprite(source);
const b = new GifSprite(source);
a.destroy(true); // WRONG: also destroys source, breaking sprite bPass true to destroy only if you know nothing else references the GifSource. Otherwise call plain destroy() and Assets.unload() when the source is no longer needed.
API Reference
Manifests
A manifest is a JSON-shaped description of every bundle in your application. Pass it to Assets.init({ manifest }) and the resolver registers every bundle at startup, so later code can reference them by name. Use manifests when you know your asset graph upfront and want one source of truth.
Quick Start
await Assets.init({
manifest: {
bundles: [
{
name: "load-screen",
assets: [{ alias: "logo", src: "logo.png" }],
},
{
name: "game",
assets: [
{ alias: "hero", src: "hero.{webp,png}" },
{ alias: "enemies", src: "enemies.json" },
],
},
],
},
});
await Assets.loadBundle("load-screen");
await Assets.loadBundle("game");Each bundle has a name and an assets array of UnresolvedAsset entries. Format expansions ({webp,png}) and resolution patterns (@{1,2}x) work inside src.
Core Patterns
Inline manifest object
const manifest = {
bundles: [
{
name: "ui",
assets: [
{ alias: "button", src: "ui/button.png" },
{ alias: "panel", src: "ui/panel.png" },
],
},
],
};
await Assets.init({ manifest });The manifest can be any plain object matching the AssetsManifest shape. Pass it directly to init.
Manifest from a URL
await Assets.init({ manifest: "assets/manifest.json" });When manifest is a string, PixiJS loads it as JSON first, then registers the bundles. Useful when your build tooling generates a manifest at deploy time (e.g., with @assetpack/core).
Format and resolution patterns in manifests
const manifest = {
bundles: [
{
name: "hero",
assets: [{ alias: "hero", src: "hero@{0.5,1,2}x.{webp,avif,png}" }],
},
],
};The resolver expands the pattern to six candidates and picks the best match based on window.devicePixelRatio and browser format support. Configure preferences in Assets.init({ texturePreference: {...} }).
Multiple aliases per entry
const manifest = {
bundles: [
{
name: "characters",
assets: [
{ alias: ["hero", "player"], src: "hero.png" },
{ alias: "npc1", src: "villager.png" },
],
},
],
};An asset can expose multiple aliases. Assets.load('hero') and Assets.load('player') return the same texture.
Data options in manifest entries
const manifest = {
bundles: [
{
name: "pixel-art",
assets: [
{
alias: "tile",
src: "tile.png",
data: { scaleMode: "nearest" },
},
],
},
],
};Each asset entry can include a data field forwarded to the loader. Use it for scaleMode, autoGenerateMipmaps, parseAsGraphicsContext, FontFace descriptors, etc.
Background-loading from a manifest
await Assets.init({ manifest });
await Assets.loadBundle("load-screen");
Assets.backgroundLoadBundle("game");
await Assets.loadBundle("game");After init, you can start background loading of other bundles immediately. The call is non-blocking; the await Assets.loadBundle('game') later resolves quickly if the background work finished.
Common Mistakes
[HIGH] Calling Assets.init twice
Wrong:
await Assets.init({ manifest });
await Assets.init({ basePath: "https://cdn.example.com/" });Correct:
await Assets.init({ manifest, basePath: "https://cdn.example.com/" });Assets.init() can only be called once. The second call is ignored with a warning. Combine all configuration into a single call.
[MEDIUM] Missing format expansion on manifest entries
Wrong:
{ alias: 'hero', src: 'hero.webp' }Correct:
{ alias: 'hero', src: 'hero.{webp,png}' }Listing only one format blocks the resolver from falling back on browsers without WebP/AVIF support. Always list at least one fallback.
[MEDIUM] Loading manifest after Assets.add
If you call Assets.add(...) or Assets.addBundle(...) before Assets.init({ manifest }), the manifest bundles will append to the existing state but preference-detection (format, resolution) hasn't run yet. Prefer calling init first, then adding ad-hoc assets.
API Reference
Progress Tracking
Assets.load and Assets.loadBundle accept a progress callback that fires as each asset completes. Use it to drive loading bars, percentage readouts, or spinner state updates during long loads. The progress value is normalized [0, 1] across the entire call.
Quick Start
const loadingBar = new Graphics();
app.stage.addChild(loadingBar);
await Assets.load(["hero.png", "enemy.png", "map.json"], (progress) => {
loadingBar.clear();
loadingBar.rect(0, 0, progress * 400, 20).fill(0x66ccff);
});The callback is invoked after each asset resolves. Final invocation reaches 1.0 just before the promise resolves.
Core Patterns
Progress on a single asset
await Assets.load("large-atlas.json", (progress) => {
console.log(`${Math.round(progress * 100)}%`);
});Even for a single asset, the callback fires (typically once, reaching 1.0). For most cases, progress for a single call is not useful; prefer progress on bundles or arrays.
Progress on an array
const assets = await Assets.load(
["hero.png", "enemy.png", "map.json", "music.mp3"],
(progress) => updateBar(progress),
);Progress is distributed evenly across the N assets. When the third asset in a four-asset array finishes, progress is 0.75.
Progress on a bundle
await Assets.loadBundle("level1", (progress) => {
loadingText.text = `Loading… ${Math.round(progress * 100)}%`;
});Each asset in the bundle contributes equally. If the bundle has 10 entries, each contributes 0.1 to the total.
Progress via LoadOptions.onProgress
await Assets.load("game.json", {
onProgress: (progress) => updateBar(progress),
onError: (err, asset) => {
const src = typeof asset === "string" ? asset : asset.src;
console.warn("failed:", src, err);
},
});Instead of a callback as the second argument, pass a LoadOptions object. This is the preferred form when you also need error handling or retry strategies. Note that onError receives string | ResolvedAsset; guard before accessing .src.
Global progress across multiple loads
async function loadAllWithTotal(tasks: Array<() => Promise<any>>) {
let done = 0;
for (const task of tasks) {
await task();
done++;
updateBar(done / tasks.length);
}
}
await loadAllWithTotal([
() => Assets.load("menu.json"),
() => Assets.loadBundle("level1"),
() => Assets.loadBundle("sounds"),
]);Progress callbacks are scoped per call. If you need a single bar across several sequential loads, wrap the calls manually and count completions yourself.
Common Mistakes
[HIGH] Treating progress callback as completion
Wrong:
Assets.load("hero.png", (progress) => {
if (progress === 1) {
showHero();
}
});Correct:
await Assets.load("hero.png", (progress) => updateBar(progress));
showHero();progress === 1 may fire slightly before the returned promise resolves. Always use the await or .then() to know when loading is truly complete. The progress callback is for UI updates only.
[MEDIUM] Progress not updating smoothly
Each asset contributes a discrete step. If your bundle has three assets, progress jumps 0 -> 0.33 -> 0.66 -> 1.0. For a smoother bar, split large bundles into smaller ones, or animate the bar width toward the target value:
let target = 0;
await Assets.loadBundle("game", (p) => {
target = p;
});
app.ticker.add(() => {
loadingBar.width += (target * maxWidth - loadingBar.width) * 0.1;
});[MEDIUM] Progress via background loading
Wrong:
Assets.backgroundLoadBundle("game", (p) => updateBar(p));Background loading has no progress callback. For visible progress, use Assets.loadBundle (foreground). See references/background.md.
API Reference
Resolution and Format Detection
PixiJS's asset resolver can pick the best asset variant for the current device based on pixel density (@0.5x, @1x, @2x) and supported image formats (avif, webp, png). Use resolution and format patterns to serve one asset spec that adapts to retina displays, format support, and bandwidth budgets.
Quick Start
await Assets.init({
texturePreference: {
resolution: window.devicePixelRatio,
format: ["avif", "webp", "png"],
},
});
Assets.add({ alias: "hero", src: "hero@{0.5,1,2}x.{webp,png}" });
const texture = await Assets.load("hero");The resolver expands the pattern to six candidates (hero@0.5x.webp, hero@0.5x.png, hero@1x.webp, ...) and picks the best match based on device pixel ratio and format support.
Core Patterns
Configuring preferences at init
await Assets.init({
texturePreference: {
resolution: window.devicePixelRatio, // e.g., 2 on retina
format: ["avif", "webp", "png"], // preferred-first order
},
});resolutionis a single number (best-match threshold) or array of acceptable resolutions.formatis ordered by preference. The resolver tries each in turn and uses the first one the browser supports.
Resolution patterns in src
Assets.add({ alias: "bg", src: "bg@{0.5,1,2}x.png" });The resolver expands this to bg@0.5x.png, bg@1x.png, bg@2x.png and picks based on texturePreference.resolution. Encoded resolution sets the texture's source resolution automatically, so the sprite's width / height appear as logical size regardless of the file chosen.
Format patterns in src
Assets.add({ alias: "icon", src: "icon.{avif,webp,png}" });Format detection runs once during Assets.init(). The resolver learns which formats the browser supports, then picks the highest-preference supported format per load.
Combined resolution + format
Assets.add({ alias: "hero", src: "hero@{0.5,1,2}x.{avif,webp,png}" });Both patterns can appear in the same src. The resolver does a Cartesian product: 3 resolutions × 3 formats = 9 candidates. It picks the best combination based on both preferences.
Manual resolution override
Assets.add({
alias: "sharp",
src: "sharp.png",
data: { resolution: 2 },
});If a file is already at a known resolution but lacks the @2x filename suffix, pass resolution in data to tell the loader. Useful when your tooling doesn't produce suffixed filenames.
Custom retina prefix
import { Resolver } from "pixi.js";
Resolver.RETINA_PREFIX = /@([0-9\.]+)density/;
Assets.add({ alias: "hero", src: "hero@{1,2}density.png" });Change the resolution filename pattern if your build pipeline uses a different convention. Default is /@([0-9\.]+)x/.
Skipping format detection
await Assets.init({
skipDetections: true,
texturePreference: { format: ["webp"] },
});If you know your target browser supports a specific format, skipDetections: true skips the runtime detection (a small init-time saving). Useful for embedded or kiosk deployments where format support is fixed.
Common Mistakes
[HIGH] Wrong pattern syntax
Wrong:
Assets.add({ alias: "hero", src: "hero@[1,2]x.png" });Correct:
Assets.add({ alias: "hero", src: "hero@{1,2}x.png" });The expansion syntax uses {} (like shell brace expansion), not []. Square brackets aren't recognized and the resolver tries to load the literal filename.
[MEDIUM] Missing fallback format
Wrong:
Assets.add({ alias: "hero", src: "hero.avif" });Correct:
Assets.add({ alias: "hero", src: "hero.{avif,png}" });If a browser doesn't support AVIF, the loader fails. Always list at least one fallback format that all target browsers support.
[MEDIUM] Mismatch between spritesheet.json meta.scale and actual image
In v8, a spritesheet's meta.scale field directly sets the resolution of the atlas texture. If the JSON says "1" but the image was exported at 2x, frames render at double the intended size. Verify meta.scale matches the actual image resolution; atlas tools like TexturePacker set this automatically.
API Reference
Spritesheet: Texture Atlases and Animations
Load and use texture atlases and animation sheets with PixiJS's Spritesheet class. Atlases reduce draw calls by packing many textures into a single image.
Quick Start
const sheet = await Assets.load("spritesheet.json");
const hero = new Sprite(sheet.textures["hero.png"]);
app.stage.addChild(hero);
const walk = new AnimatedSprite(sheet.animations["walk"]);
walk.animationSpeed = 0.15;
walk.play();
app.stage.addChild(walk);When loaded through Assets.load(), the JSON file is fetched, the atlas image is loaded, and sheet.parse() is called automatically. The returned object is a Spritesheet instance.
Core Patterns
SpritesheetData JSON format
{
"frames": {
"hero.png": {
"frame": { "x": 0, "y": 0, "w": 64, "h": 64 },
"sourceSize": { "w": 64, "h": 64 },
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
"anchor": { "x": 0.5, "y": 0.5 },
"borders": { "left": 10, "top": 10, "right": 10, "bottom": 10 }
},
"walk_01.png": {
"frame": { "x": 64, "y": 0, "w": 64, "h": 64 },
"rotated": true,
"trimmed": true,
"sourceSize": { "w": 80, "h": 80 },
"spriteSourceSize": { "x": 8, "y": 8, "w": 64, "h": 64 }
}
},
"animations": {
"walk": ["walk_01.png", "walk_02.png", "walk_03.png"]
},
"meta": {
"image": "spritesheet.png",
"size": { "w": 512, "h": 256 },
"scale": "1"
}
}Key fields:
framesmaps frame names to rectangle data, trim info, anchors, and 9-slice borders.rotatedindicates the frame is stored rotated 90 degrees in the atlas. The parser swaps width/height automatically.trimmedindicates transparent padding was removed.sourceSizeis the original dimensions;spriteSourceSizeis the trimmed region within it.animationsmaps animation names to ordered arrays of frame names.meta.scalesets the resolution of the texture source."2"means the atlas is @2x.meta.imageis the atlas image filename, resolved relative to the JSON file.
Manual Spritesheet creation
const texture = await Assets.load("atlas.png");
const sheet = new Spritesheet({
texture,
data: spritesheetJsonData,
cachePrefix: "myAtlas_",
});
await sheet.parse();
const frame = new Sprite(sheet.textures["hero.png"]);parse() is async for large spritesheets (over 1000 frames); it batches texture creation across multiple frames. For smaller sheets, parseSync() is also available.
cachePrefix prepends a string to all cached texture names, preventing collisions when multiple atlases share frame names (e.g., "hero.png" in two different sheets).
Preloaded texture with Assets
const atlasTexture = await Assets.load("images/spritesheet.png");
Assets.add({
alias: "atlas",
src: "images/spritesheet.json",
data: { texture: atlasTexture },
});
const sheet = await Assets.load("atlas");Multi-pack spritesheets
When meta.related_multi_packs is present, the loader automatically loads and links related spritesheets. All linked sheets are accessible via sheet.linkedSheets.
Cleanup
sheet.destroy();
sheet.destroy(true); // also destroys the base atlas textureCommon Mistakes
[HIGH] Spritesheet meta.scale behavior change
In v8, meta.scale directly sets the texture source resolution. If your atlas was exported at @2x but meta.scale says "1", frames render at double the intended size. Ensure meta.scale matches the actual resolution of the atlas image. Tools like TexturePacker set this automatically.
[MEDIUM] Not awaiting spritesheet parse
parse() is async. Accessing sheet.textures before it resolves returns undefined entries. When loading via Assets.load(), parsing is handled automatically.
API Reference
SVG Loading
PixiJS loads .svg files in one of two modes: rasterized to a texture (fast, fixed resolution) or parsed into a GraphicsContext (scalable vector, reusable across Graphics instances). Choose texture mode for backgrounds and decorative elements at a fixed size; choose Graphics mode for icons, UI elements, or anything that needs to scale or be modified at runtime.
Quick Start
const svgTexture = await Assets.load("icon.svg");
const sprite = new Sprite(svgTexture);
const svgContext = await Assets.load({
src: "icon.svg",
data: { parseAsGraphicsContext: true },
});
const graphic = new Graphics(svgContext);By default, SVG files rasterize to a texture at their native size. Pass parseAsGraphicsContext: true in the data field to parse as vector geometry instead.
Core Patterns
Texture mode (default)
const icon = await Assets.load("close.svg");
const button = new Sprite(icon);The SVG is rasterized to a bitmap via the browser's native SVG-to-image pipeline, then uploaded as a Texture. Rendering is batched just like a regular sprite. Scaling up beyond the rasterized resolution pixelates; the rasterization happens once.
Graphics mode
const context = await Assets.load({
src: "logo.svg",
data: { parseAsGraphicsContext: true },
});
const small = new Graphics(context);
small.scale.set(0.5);
const large = new Graphics(context);
large.scale.set(3);The SVG is parsed into a GraphicsContext (a compiled list of fill/stroke instructions). Both Graphics instances share the same context; cheap, and both stay crisp at any scale.
Resolution for sharper texture mode
const icon = await Assets.load({
src: "icon.svg",
data: { resolution: 2 },
});For texture mode, you can pass resolution in data to rasterize at a higher density. Pairs with autoGenerateMipmaps for smooth downscaling. Doesn't apply in Graphics mode.
Global default
import { loadSvg } from "pixi.js";
loadSvg.config.parseAsGraphicsContext = true;Flip the default mode globally. Any subsequent Assets.load('*.svg') call parses as Graphics unless overridden in the asset's data options.
When to use which mode
| Mode | Best for | Cost |
|---|---|---|
| Texture (default) | Icons at a known size, backgrounds, complex SVGs | One-time rasterization; fast per-frame; pixelates when scaled |
Graphics (parseAsGraphicsContext: true) | Scalable icons, UI elements, runtime modification | Higher initial parse cost; crisp at any scale; shared contexts |
If you need the same SVG at multiple sizes, Graphics mode is usually faster because you parse once and scale cheaply. If you have one fixed-size SVG, texture mode is simpler and batches with other sprites.
Common Mistakes
[HIGH] Expecting scalability in texture mode
Wrong:
const icon = await Assets.load("icon.svg");
const sprite = new Sprite(icon);
sprite.scale.set(5); // pixelatesCorrect (if you need scaling):
const context = await Assets.load({
src: "icon.svg",
data: { parseAsGraphicsContext: true },
});
const graphic = new Graphics(context);
graphic.scale.set(5); // crispOr rasterize at higher resolution:
const icon = await Assets.load({
src: "icon.svg",
data: { resolution: 5 },
});Texture mode is frozen at rasterization time. Use Graphics mode or high resolution when the final size isn't known upfront.
[MEDIUM] External references in SVG files
SVGs that reference external images via <image href="..."> may fail to load if the reference is cross-origin without CORS headers. Inline the image data as base64 or serve everything from the same origin.
[MEDIUM] CSS in <style> elements
Not all CSS features are supported in Graphics mode; the parser extracts geometry, fill, and stroke, but ignores advanced CSS like filter or mask. For full CSS fidelity, use texture mode.
API Reference
Video Textures
PixiJS loads videos through Assets.load() and returns a regular Texture. Pass the texture to a Sprite and it paints the current video frame each render. Use video textures for backgrounds, cutscenes, or any animated surface driven by a video source.
Supported extensions: .mp4, .m4v, .webm, .ogg, .ogv, .h264, .avi, .mov. Browser support varies; always provide a fallback format.
Quick Start
const videoTex = await Assets.load("intro.mp4");
const sprite = new Sprite(videoTex);
app.stage.addChild(sprite);The returned texture wraps a VideoSource. The video starts playing automatically unless you set autoPlay: false in the data options.
Core Patterns
Load options via data
const texture = await Assets.load({
src: "city.mp4",
data: {
autoPlay: false,
loop: true,
muted: true,
preload: true,
playsinline: true,
updateFPS: 30,
alphaMode: "premultiply-alpha-on-upload",
},
});data accepts VideoSourceOptions fields. Defaults come from VideoSource.defaultOptions.
| Option | Default | Purpose |
|---|---|---|
autoLoad | true | Start downloading the video as soon as it's assigned |
autoPlay | true | Start playing once canplay fires |
loop | false | Restart at the end |
muted | true | Required for autoplay on most browsers |
playsinline | true | Prevents iOS fullscreen takeover |
preload | false | Await canplaythrough before resolving |
updateFPS | 0 | Texture update rate. 0 updates every render frame |
crossorigin | true | CORS mode for cross-origin URLs |
alphaMode | auto-detected | 'no-premultiply-alpha' for straight alpha |
mime | derived from extension | Force a specific MIME type when the URL lacks an extension |
Format fallback
Assets.add({
alias: "clip",
src: "clip.{webm,mp4}",
});
const texture = await Assets.load("clip");List multiple formats so the resolver picks the first one the browser supports. WebM and MP4 together cover all current browsers.
Mobile autoplay
const texture = await Assets.load({
src: "ad.mp4",
data: {
muted: true,
playsinline: true,
autoPlay: true,
},
});iOS and mobile Chrome only autoplay muted, inline video. Set both muted: true and playsinline: true or playback stalls on the first frame until the user taps.
Manual playback control
const texture = await Assets.load({
src: "boss-fight.mp4",
data: { autoPlay: false, preload: true },
});
const videoSource = texture.source as VideoSource;
startButton.on("pointertap", () => {
videoSource.resource.play();
});With autoPlay: false, grab the underlying VideoSource from texture.source and call .play(), .pause(), or .currentTime = on its resource (the HTMLVideoElement). Pair with preload: true to guarantee the first frame is uploaded before you call play().
Updating at a fixed rate
await Assets.load({
src: "background.mp4",
data: { updateFPS: 15 },
});updateFPS caps how often the GPU texture re-uploads from the video element. 0 re-uploads every render tick (smoothest; highest cost). 15 caps it to 15 uploads per second, which saves bandwidth on static-ish video backgrounds.
Forcing the video parser
If your video URL lacks an extension (e.g., a CDN signed URL or a blob), the loader can't pick a parser by test. Force it with the top-level parser field:
const texture = await Assets.load({
src: "https://cdn.example.com/stream/abc123",
parser: "video",
data: { mime: "video/mp4" },
});See the main SKILL.md section on "Forcing a parser with parser" for the full list of parser IDs.
Common Mistakes
[HIGH] Autoplay without mute on mobile
Wrong:
await Assets.load({ src: "ad.mp4", data: { autoPlay: true } });Correct:
await Assets.load({
src: "ad.mp4",
data: { autoPlay: true, muted: true, playsinline: true },
});Mobile browsers block autoplay with sound. The promise resolves, but the video stays frozen on frame 0 until the user interacts.
[MEDIUM] Expecting sprite.texture to update itself
The Sprite reflects the current video frame automatically because VideoSource re-uploads on every render. You don't need to call update() yourself. If playback looks frozen, check that the underlying video element is actually playing (videoSource.resource.paused should be false).
[MEDIUM] Missing CORS for cross-origin video
Video served from another origin without Access-Control-Allow-Origin headers taints the canvas and most WebGL operations (readPixels, snapshots) fail silently. Either serve with CORS headers or pass data: { crossorigin: false } and accept that the texture can't be extracted.
API Reference
Related skills
How it compares
Use pixijs-assets for staged bundle preloading; switch to pixijs-performance when loaded assets still cause FPS or memory issues at runtime.
FAQ
Can Texture.from load a remote URL in v8?
No. Texture.from only reads the cache; use await Assets.load(url) first and use the returned texture.
When is the parser field needed?
For extension-less signed CDN, blob, or API URLs where auto-detection cannot pick the loader.
How should level transitions free memory?
Call Assets.unload or unloadBundle when leaving a screen so cached textures do not accumulate on the GPU.
Is Pixijs Assets safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.