pixijs-application

Use this skill when creating and configuring a PixiJS v8 Application. Covers new Application() + async app.init() options (width, height, background,…

INSTALLATION
npx skills add https://github.com/pixijs/pixijs-skills --skill pixijs-application
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

$2c

document.body.appendChild(app.canvas);

**Related skills:** `pixijs-core-concepts` (renderers, render pipeline), `pixijs-ticker` (render loop detail), `pixijs-scene-container` (working with `app.stage`), `pixijs-environments` (non-browser setups).

## Core Patterns

### Lifecycle: construct, init, render, destroy

import { Application } from "pixi.js";

const app = new Application();

await app.init({ width: 800, height: 600 });

document.body.appendChild(app.canvas);

// ... run scene, ticker drives app.render() automatically ...

app.destroy(

{ removeView: true, releaseGlobalResources: true },

{ children: true, texture: true, textureSource: true },

);


- `new Application()` allocates the instance but creates nothing. Options passed here are ignored with a v8 deprecation warning.

- `app.init(options)` is async. It builds the renderer, wires up plugins, and must complete before you can use `app.canvas`, `app.renderer`, or `app.screen`.

- The TickerPlugin calls `app.render()` every frame once init resolves (unless `autoStart: false`).

- `app.destroy(rendererDestroyOptions, stageDestroyOptions)` — the first argument forwards to `renderer.destroy()`. Pass `true` or `{ removeView: true }` to remove the canvas from the DOM. Add `releaseGlobalResources: true` to drain global pools (batches, texture caches) when tearing down and re-creating an app in the same tab; omitting it is the usual cause of flickering and stale textures after a re-init (see `pixijs-performance`).

### Key init options

await app.init({

width: 800,

height: 600,

background: 0x1099bb,

backgroundAlpha: 1,

antialias: true,

resolution: window.devicePixelRatio,

autoDensity: true,

preference: "webgpu",

autoStart: true,

sharedTicker: false,

resizeTo: window,

canvas: document.querySelector("#game-canvas") as HTMLCanvasElement,

});


For every option — view/canvas, background, renderer preference (including the array form), ticker, resize, culler, events, accessibility, WebGL/WebGPU context flags, Graphics bezier smoothness, GC, and per-renderer overrides (`webgl` / `webgpu` / `canvasOptions`) — see [references/application-options.md](https://github.com/pixijs/pixijs-skills/blob/HEAD/skills/pixijs-application/references/application-options.md).

### Application properties

app.stage; // root Container; add all display objects here

app.renderer; // the WebGL/WebGPU/Canvas renderer instance

app.canvas; // the HTMLCanvasElement (insert it into the DOM yourself)

app.screen; // Rectangle describing the visible area in CSS pixels

app.domContainerRoot; // HTMLDivElement that holds DOMContainer overlays


`app.stage` is a plain `Container`. For scene graph detail (transforms, addChild, destroy) see `pixijs-scene-container`. For renderer-level operations (extract, generateTexture, custom systems) see `pixijs-core-concepts` and `pixijs-custom-rendering`. `app.domContainerRoot` is the `<div>` that the renderer uses to host `DOMContainer` overlays; append it next to `app.canvas` when you need DOM elements pinned to scene nodes (see `pixijs-scene-dom-container`).

### ResizePlugin

Set `resizeTo` at init (or reassign `app.resizeTo` later) to have the plugin listen for the `resize` event and call `renderer.resize()` with the target element's client size. Combine with `autoDensity: true` and `resolution: window.devicePixelRatio` for high-DPI output.

await app.init({ resizeTo: window });

app.resizeTo = document.querySelector("#game-container") as HTMLElement;

app.resize(); // immediate resize to the target's current size

app.queueResize(); // defer the resize to the next animation frame

app.cancelResize(); // drop a pending queueResize


The plugin keeps the canvas matched to the target. `app.screen` and `app.canvas.width/height` update in response; read them after the resize to place UI.

- `app.resize()` — immediate synchronous resize.

- `app.queueResize()` — coalesces rapid calls by deferring to the next frame; internally used by the `window.resize` listener to avoid redundant work.

- `app.cancelResize()` — cancels a queued resize. Call this before tearing down your own layout code that triggered `queueResize`.

### Ticker basics

The TickerPlugin creates `app.ticker` and registers `app.render()` on it at `UPDATE_PRIORITY.LOW`. Control the loop with `app.start()`/`app.stop()` and add callbacks with `app.ticker.add` / `app.ticker.addOnce`:

app.ticker.add((ticker) => {

sprite.rotation += 0.01 * ticker.deltaTime;

});

app.ticker.addOnce(() => {

console.log("runs once on the next frame, then removes itself");

});

app.stop(); // pause the render loop (e.g. tab hidden)

app.start(); // resume


The callback receives the `Ticker` instance; read `ticker.deltaTime` for a frame-rate-independent multiplier (~1.0 at 60fps), `ticker.deltaMS` for real milliseconds, or `ticker.FPS` for the current frame rate. See `pixijs-ticker` for priorities, FPS capping, `onRender`, shared vs private tickers, and the v8 callback signature change.

### Manual render loop

await app.init({ autoStart: false, width: 800, height: 600 });

document.body.appendChild(app.canvas);

function frame() {

updateScene();

app.render();

requestAnimationFrame(frame);

}

frame();


`autoStart: false` prevents the TickerPlugin from starting the ticker automatically. Call `app.render()` yourself (or `app.renderer.render({ container: app.stage })` for the same effect). If you still want registered ticker callbacks to fire, call `app.ticker.update()` inside your loop before `app.render()`.

### CullerPlugin (opt-in)

The CullerPlugin skips rendering containers that fall outside `app.renderer.screen`. It isn't registered by default; add it before creating your app:

import {

Application,

Container,

Sprite,

extensions,

CullerPlugin,

Rectangle,

} from "pixi.js";

extensions.add(CullerPlugin);

const app = new Application();

await app.init({ width: 800, height: 600 });

const world = new Container();

world.cullable = true; // this container is culled when its bounds leave the screen

world.cullableChildren = true; // default; set false to skip recursing into children

const tile = Sprite.from("tile.png");

tile.cullable = true;

world.addChild(tile);

app.stage.addChild(world);


Containers are not culled unless `cullable` is set. Override the default bounds check with `container.cullArea = new Rectangle(x, y, w, h)` when child bounds are expensive to compute. The plugin wraps `app.render()` so `Culler.shared.cull(app.stage, app.renderer.screen)` runs before every frame. See `pixijs-performance` for when culling pays off.

### Custom Application plugins

Extend `Application` by registering a class with `static init`, `static destroy`, and `static extension = ExtensionType.Application`. Both methods are called with `this` bound to the Application instance, so `this.renderer` and `this.stage` are available.

import {

Application,

ExtensionType,

extensions,

type ApplicationOptions,

} from "pixi.js";

class FpsOverlay {

public static extension = ExtensionType.Application;

public static init(this: Application, options: Partial<ApplicationOptions>) {

// runs inside app.init() after the renderer is created

// attach props/methods to this to expose them on the app

}

public static destroy(this: Application) {

// runs inside app.destroy() — tear down anything you attached

}

}

extensions.add(FpsOverlay);


Plugins initialize in registration order and destroy in reverse. To add typed options for your plugin, extend `PixiMixins.ApplicationOptions`:

declare global {

namespace PixiMixins {

interface ApplicationOptions {

fpsOverlay?: { visible?: boolean };

}

}

}

await app.init({ fpsOverlay: { visible: true } });


The built-in `ResizePlugin`, `TickerPlugin`, and opt-in `CullerPlugin` all use this same contract. If you set `skipExtensionImports: true`, register the built-ins you need yourself (`extensions.add(ResizePlugin, TickerPlugin)`).

## Common Mistakes

### [CRITICAL] Passing options to the constructor

Wrong:

const app = new Application({ width: 800, height: 600 });

document.body.appendChild(app.canvas);


Correct:

const app = new Application();

await app.init({ width: 800, height: 600 });

document.body.appendChild(app.canvas);


In v8 the `Application` constructor takes no arguments. Options passed there are ignored and log a deprecation warning; the renderer is only created inside the async `init()` call.

### [HIGH] Using app.view instead of app.canvas

Wrong:

document.body.appendChild(app.view);


Correct:

document.body.appendChild(app.canvas);


`app.view` was renamed to `app.canvas` in v8. The old getter still works but emits a deprecation warning.

### [MEDIUM] Touching app.canvas or app.renderer before init resolves

Wrong:

const app = new Application();

document.body.appendChild(app.canvas);

app.init({ width: 800, height: 600 });


Correct:

const app = new Application();

await app.init({ width: 800, height: 600 });

document.body.appendChild(app.canvas);

BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card