Application is the convenience wrapper that owns a renderer, a root stage Container, a canvas, and the Ticker/Resize plugins. In v8 the constructor takes no arguments; all configuration is passed to the async app.init() call which instantiates the renderer via autoDetectRenderer.
Quick Start
tsimport { Application } from "pixi.js"; const app = new Application(); await app.init({ resizeTo: window, background: "#1099bb", antialias: true, preference: "webgl", autoDensity: true, resolution: window.devicePixelRatio, }); 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
tsimport { 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 useapp.canvas,app.renderer, orapp.screen.- The TickerPlugin calls
app.render()every frame once init resolves (unlessautoStart: false). app.destroy(rendererDestroyOptions, stageDestroyOptions)— the first argument forwards torenderer.destroy(). Passtrueor{ removeView: true }to remove the canvas from the DOM. AddreleaseGlobalResources: trueto 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 (seepixijs-performance).
Key init options
tsawait 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.
Application properties
tsapp.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.
tsawait 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 thewindow.resizelistener to avoid redundant work.app.cancelResize()— cancels a queued resize. Call this before tearing down your own layout code that triggeredqueueResize.
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:
tsapp.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
tsawait 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:
tsimport { 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.
tsimport { 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:
tsdeclare 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:
tsconst app = new Application({ width: 800, height: 600 }); document.body.appendChild(app.canvas);
Correct:
tsconst 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:
tsdocument.body.appendChild(app.view);
Correct:
tsdocument.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:
tsconst app = new Application(); document.body.appendChild(app.canvas); app.init({ width: 800, height: 600 });
Correct:
tsconst app = new Application(); await app.init({ width: 800, height: 600 }); document.body.appendChild(app.canvas);
app.renderer, app.canvas, and app.screen are only populated once the init() promise resolves. Accessing them earlier returns undefined.


