# PixiJS Documentation for LLMs > PixiJS is the fastest, most lightweight 2D library available for the web, working across all devices and allowing you to create rich, interactive graphics and cross-platform applications using WebGL and WebGPU. This file contains all documentation content in a single document following the llmtxt.org standard. ## Ecosystem PixiJS itself is just a rendering engine. However, there is a foundation of a robust ecosystem of libraries and tools that enhance and expand its capabilities. These tools integrate seamlessly with PixiJS, empowering developers to create richer, more interactive applications with ease. ## Core Ecosystem Libraries ### [DevTools](https://pixijs.io/devtools/) Optimize and debug your PixiJS projects with DevTools. This browser extension offers real-time insights into application performance, rendering hierarchies, and texture management, ensuring your projects run smoothly. ### [React Integration](https:/react.pixijs.io/) :::info PixiJS React requires React 19 or higher. ::: Simplify the use of PixiJS in React applications with the Pixi-React library. This library provides bindings that allow you to manage PixiJS components as React elements, making it easy to incorporate powerful graphics into React's declarative framework. ### [Layout](https://layout.pixijs.io/) Add flexbox-style layouting to PixiJS with the PixiJS Layout library, which is powered by Facebook’s [Yoga](https://www.yogalayout.dev/) engine. It introduces a declarative way to control positioning, alignment, and sizing of PixiJS display objects using familiar CSS-like rules. Key features include: - Built on Yoga for standardized, reliable layouts - Fully opt-in: apply layout only where you need it - Any PixiJS object can now be layout-aware - Supports PixiJS React - New web-style features: objectFit, objectPosition, and overflow scrolling ### [Spine Integration](https://esotericsoftware.com/spine-pixi) Bring animations to life with Spine-Pixi. This integration combines the power of PixiJS and Spine, a leading animation tool, to create smooth, skeletal-based animations for games and interactive content. ### [Filters](https://github.com/pixijs/filters) Transform your visuals with PixiJS Filters. This extensive collection of high-performance effects includes options like blur, glow, and color adjustments, giving you the tools to create visually stunning graphics. ### [Sound](https://github.com/pixijs/sound) Add audio to your projects with PixiJS Sound a WebAudio API playback library, with filters. ### [UI](https://github.com/pixijs/ui) Streamline the creation of user interfaces with PixiJS UI. This library offers pre-built components: - Buttons - Sliders - Progress bars - Lists - Scrollbox - Radio Groups - Checkboxes - Switches All the essentials for building interactive interfaces in PixiJS. ### [AssetPack](https://pixijs.io/assetpack/) Simplify asset management with AssetPack. This tool organizes, packages, and loads assets efficiently, reducing load times and improving resource handling for your projects. ## [PixiJS Userland](https://github.com/pixijs-userland) - Community-Driven Repositories PixiJS Userland is a dedicated space for hosting community-driven repositories. This organization allows developers to collaborate on PixiJS-related projects and share their work with the wider community. If you have an idea for a new library or tool, you can request access to PixiJS Userland to create and maintain a repository within the organization. This is a great opportunity to contribute to the growing PixiJS ecosystem and engage with like-minded developers. Note that userland repositories are community-driven and may not be up to date with the latest PixiJS releases. However, they offer a wealth of resources and inspiration for developers looking to enhance their PixiJS projects. ## Getting Started with the Ecosystem To explore these libraries, visit their respective documentation and GitHub repositories for installation instructions and usage guides. Additionally, PixiJS offers [**Creation Templates**](https://pixijs.io/create-pixi/docs/guide/creations/intro/) through the [PixiJS Create CLI](https://pixijs.io/create-pixi/) that combine many of these libraries into pre-configured setups, ideal for specific use cases and platforms. For inspiration, you can also check out the [open-games repository](https://github.com/pixijs/open-games), which showcases a variety of games built with PixiJS and its ecosystem libraries. --- ## Quick Start # Quick Start --- ## Try PixiJS Online - To quickly get a taste of PixiJS, you can try it directly in our [PixiJS Playground](/8.x/playground). --- ## Creating a New Project :::info[Prerequisites] - Familiarity with the command line and a basic understanding of JavaScript. - Install [Node.js](https://nodejs.org/en/) v20.0 or higher. ::: In this section, we will introduce how to scaffold a PixiJS application on your local machine. The created project will use a pre-configured build setup, allowing you to quickly get started with PixiJS development. Make sure your current working directory is where you want to create your project. Run the following command in your terminal: ```sh npm create pixi.js@latest ``` This command will install and execute the [PixiJS Create](https://pixijs.io/create-pixi/) CLI and begin scaffolding your project. You will be prompted to configure your project by selecting various options, including selecting a template type for setting up your project. There are two main types of templates to choose from: #### Creation Templates (Recommended) Creation templates are tailored for specific platforms and include additional configurations and dependencies to streamline development for a particular use case. These templates are more opinionated and are perfect for beginners or those looking for a ready-to-go setup. #### Bundler Templates Bundler templates are general templates designed to scaffold a PixiJS project with a specific bundler. They include the necessary configurations and dependencies but leave the project structure flexible, making them ideal for experienced developers who prefer more control. We recommended using the Vite + PixiJS template for most projects when using bundler templates, as it provides a modern and fast setup for PixiJS applications with minimal configuration. After selecting your desired template, the scaffolding tool will create a new project directory with the chosen configuration. Navigate to the project directory and install the dependencies: ```bash cd npm install npm run dev ``` You can also add PixiJS to an existing project: ```bash npm install pixi.js ``` ## Usage Once you've set up your project, here's a simple example to get started with PixiJS: ```ts import { Application, Assets, Container, Sprite } from 'pixi.js'; (async () => { // Create a new application const app = new Application(); // Initialize the application await app.init({ background: '#1099bb', resizeTo: window }); // Append the application canvas to the document body document.body.appendChild(app.canvas); // Create and add a container to the stage const container = new Container(); app.stage.addChild(container); // Load the bunny texture const texture = await Assets.load('https://pixijs.com/assets/bunny.png'); // Create a 5x5 grid of bunnies in the container for (let i = 0; i < 25; i++) { const bunny = new Sprite(texture); bunny.x = (i % 5) * 40; bunny.y = Math.floor(i / 5) * 40; container.addChild(bunny); } // Move the container to the center container.x = app.screen.width / 2; container.y = app.screen.height / 2; // Center the bunny sprites in local container coordinates container.pivot.x = container.width / 2; container.pivot.y = container.height / 2; // Listen for animate update app.ticker.add((time) => { // Continuously rotate the container! // * use delta to create frame-independent transform * container.rotation -= 0.01 * time.deltaTime; }); })(); ``` :::warning If using Vite you still need to wrap your code in an async function. There is an issue when using top level await with PixiJS when building for production. This issue is known to affect Vite \<=6.0.6. Future versions of Vite may resolve this issue. ::: --- ## Architecture # Architecture Here's a list of the major components that make up PixiJS. Note that this list isn't exhaustive. Additionally, don't worry too much about how each component works. The goal here is to give you a feel for what's under the hood as we start exploring the engine. ### Major Components | Component | Description | | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Renderer** | The core of the PixiJS system is the renderer, which displays the scene graph and draws it to the screen. PixiJS will automatically determine whether to provide you the WebGPU or WebGL renderer under the hood. | | **Container** | Main scene object which creates a scene graph: the tree of renderable objects to be displayed, such as sprites, graphics and text. See [Scene Graph](scene-graph) for more details. | | **Assets** | The Asset system provides tools for asynchronously loading resources such as images and audio files. | | **Ticker** | Tickers provide periodic callbacks based on a clock. Your game update logic will generally be run in response to a tick once per frame. You can have multiple tickers in use at one time. | | **Application** | The Application is a simple helper that wraps a Loader, Ticker and Renderer into a single, convenient easy-to-use object. Great for getting started quickly, prototyping and building simple projects. | | **Events** | PixiJS supports pointer-based interaction - making objects clickable, firing hover events, etc. | | **Accessibility** | Woven through our display system is a rich set of tools for enabling keyboard and screen-reader accessibility. | | **Filters** | PixiJS supports a variety of filters, including custom shaders, to apply effects to your renderable objects. | ## Extensions PixiJS v8 is built entirely around the concept of extensions. Every system in PixiJS is implemented as a modular extension. This allows PixiJS to remain lightweight, flexible, and easy to extend. :::info In most cases, you won’t need to interact with the extension system directly unless you are developing a third-party library or contributing to the PixiJS ecosystem itself. ::: ## Extension Types PixiJS supports a wide range of extension types, each serving a unique role in the architecture: ### Assets - `ExtensionType.Asset`: Groups together loaders, resolvers, cache and detection extensions into one convenient object instead of having to register each one separately. - `ExtensionType.LoadParser`: Loads resources like images, JSON, videos. - `ExtensionType.ResolveParser`: Converts asset URLs into a format that can be used by the loader. - `ExtensionType.CacheParser`: Determines caching behavior for a particular asset. - `ExtensionType.DetectionParser`: Identifies asset format support on the current platform. ### Renderer (WebGL, WebGPU, Canvas) - `ExtensionType.WebGLSystem`, `ExtensionType.WebGPUSystem`, `ExtensionType.CanvasSystem`: Add systems to their respective renderers. These systems can vary widely in functionality, from managing textures to accessibility features. - `ExtensionType.WebGLPipes`, `ExtensionType.WebGPUPipes`, `ExtensionType.CanvasPipes`: Add a new rendering pipe. RenderPipes are specifically used to render Renderables like a Mesh - `ExtensionType.WebGLPipesAdaptor`, `ExtensionType.WebGPUPipesAdaptor`, `ExtensionType.CanvasPipesAdaptor`: Adapt rendering pipes for the respective renderers. ### Application - `ExtensionType.Application`: Used for plugins that extend the `Application` lifecycle. For example the `TickerPlugin` and `ResizePlugin` are both application extensions. ### Environment - `ExtensionType.Environment`: Used to detect and configure platform-specific behavior. This can be useful for configuring PixiJS to work in environments like Node.js, Web Workers, or the browser. ### Other (Primarily Internal Use) These extension types are mainly used internally and are typically not required in most user-facing applications: - `ExtensionType.MaskEffect`: Used by MaskEffectManager for custom masking behaviors. - `ExtensionType.BlendMode`: A type of extension for creating a new advanced blend mode. - `ExtensionType.TextureSource`: A type of extension that will be used to auto detect a resource type E.g `VideoSource` - `ExtensionType.ShapeBuilder`: A type of extension for building and triangulating custom shapes used in graphics. - `ExtensionType.Batcher`: A type of extension for creating custom batchers used in rendering. ## Creating Extensions The `extensions` object in PixiJS is a global registry for managing extensions. Extensions must declare an `extension` field with metadata, and are registered via `extensions.add(...)`. ```ts import { extensions, ExtensionType } from 'pixi.js'; const myLoader = { extension: { type: ExtensionType.LoadParser, name: 'my-loader', }, test(url) { /* logic */ }, load(url) { /* logic */ }, }; extensions.add(myLoader); ``` --- ## Environments # Using PixiJS in Different Environments PixiJS is a highly adaptable library that can run in a variety of environments, including browsers, Web Workers, and custom execution contexts like Node.js. This guide explains how PixiJS handles different environments and how you can configure it to suit your application's needs. ## Running PixiJS in the Browser For browser environments, PixiJS uses the `BrowserAdapter` by default, you should not need to configure anything ### Example: ```typescript import { Application } from 'pixi.js'; const app = new Application(); await app.init({ width: 800, height: 600, }); document.body.appendChild(app.canvas); ``` ## Running PixiJS in Web Workers Web Workers provide a parallel execution environment, ideal for offloading rendering tasks. PixiJS supports Web Workers using the `WebWorkerAdapter`: ### Example: ```typescript import { DOMAdapter, WebWorkerAdapter } from 'pixi.js'; // Must be set before creating anything in PixiJS DOMAdapter.set(WebWorkerAdapter); const app = new Application(); await app.init({ width: 800, height: 600, }); app.canvas; // OffscreenCanvas ``` ## Custom Environments For non-standard environments, you can create a custom adapter by implementing the `Adapter` interface. This allows PixiJS to function in environments like Node.js or headless testing setups. ### Example Custom Adapter: ```typescript import { DOMAdapter } from 'pixi.js'; const CustomAdapter = { createCanvas: (width, height) => { /* custom implementation */ }, getCanvasRenderingContext2D: () => { /* custom implementation */ }, getWebGLRenderingContext: () => { /* custom implementation */ }, getNavigator: () => ({ userAgent: 'Custom', gpu: null }), getBaseUrl: () => 'custom://', fetch: async (url, options) => { /* custom fetch */ }, parseXML: (xml) => { /* custom XML parser */ }, }; DOMAdapter.set(CustomAdapter); ``` --- ## Garbage Collection # Managing Garbage Collection in PixiJS Efficient resource management is crucial for maintaining optimal performance in any PixiJS application. This guide explores how PixiJS handles garbage collection, the tools it provides, and best practices for managing GPU resources effectively. ## Explicit Resource Management with `destroy` PixiJS objects, such as textures, meshes, and other GPU-backed data, hold references that consume memory. To explicitly release these resources, call the `destroy` method on objects you no longer need. For example: ```javascript import { Sprite } from 'pixi.js'; const sprite = new Sprite(texture); // Use the sprite in your application // When no longer needed sprite.destroy(); ``` Calling `destroy` ensures that the object’s GPU resources are freed immediately, reducing the likelihood of memory leaks and improving performance. ## Managing Textures with `texture.unload` In cases where PixiJS’s automatic texture garbage collection is insufficient, you can manually unload textures from the GPU using `texture.unload()`: ```javascript import { Texture } from 'pixi.js'; const texture = Texture.from('image.png'); // Use the texture // When no longer needed texture.unload(); ``` This is particularly useful for applications that dynamically load large numbers of textures and require precise memory control. ## Automatic Texture Garbage Collection with `TextureGCSystem` PixiJS also includes the `TextureGCSystem`, a system that manages GPU texture memory. By default: - **Removes textures unused for 3600 frames** (approximately 1 hour at 60 FPS). - **Checks every 600 frames** for unused textures. ### Customizing `TextureGCSystem` You can adjust the behavior of `TextureGCSystem` to suit your application: - **`textureGCActive`**: Enable or disable garbage collection. Default: `true`. - **`textureGCMaxIdle`**: Maximum idle frames before texture cleanup. Default: `3600` frames. - **`textureGCCheckCountMax`**: Frequency of garbage collection checks (in frames). Default: `600` frames. Example configuration: ```javascript import { Application } from 'pixi.js'; const app = new Application(); await app.init({ textureGCActive: true, // Enable texture garbage collection textureGCMaxIdle: 7200, // 2 hours idle time textureGCCheckCountMax: 1200, // Check every 20 seconds at 60 FPS }); ``` ## Best Practices for Garbage Collection in PixiJS 1. **Explicitly Destroy Objects:** Always call `destroy` on objects you no longer need to ensure GPU resources are promptly released. 2. **Use Pooling:** Reuse objects with a pooling system to reduce allocation and deallocation overhead. 3. **Proactively Manage Textures:** Use `texture.unload()` for manual memory management when necessary. By following these practices and understanding PixiJS’s garbage collection mechanisms, you can create high-performance applications that efficiently utilize system resources. --- ## Performance Tips # Performance Tips ### General - Only optimize when you need to! PixiJS can handle a fair amount of content off the bat - Be mindful of the complexity of your scene. The more objects you add the slower things will end up - Order can help, for example sprite / graphic / sprite / graphic is slower than sprite / sprite / graphic / graphic - Some older mobile devices run things a little slower. Passing in the option `useContextAlpha: false` and `antialias: false` to the Renderer or Application can help with performance - Culling is disabled by default as it's often better to do this at an application level or set objects to be `cullable = true`. If you are GPU-bound it will improve performance; if you are CPU-bound it will degrade performance ### Sprites - Use Spritesheets where possible to minimize total textures - Sprites can be batched with up to 16 different textures (dependent on hardware) - This is the fastest way to render content - On older devices use smaller low resolution textures - Add the extention `@0.5x.png` to the 50% scale-down spritesheet so PixiJS will visually-double them automatically - Draw order can be important ### Graphics - Graphics objects are fastest when they are not modified constantly (not including the transform, alpha or tint!) - Graphics objects are batched when under a certain size (100 points or smaller) - Small Graphics objects are as fast as Sprites (rectangles, triangles) - Using 100s of graphics complex objects can be slow, in this instance use sprites (you can create a texture) ### Texture - Textures are automatically managed by a Texture Garbage Collector - You can also manage them yourself by using `texture.destroy()` - If you plan to destroy more than one at once add a random delay to their destruction to remove freezing - Delay texture destroy if you plan to delete a lot of textures yourself ### Text - Avoid changing it on every frame as this can be expensive (each time it draws to a canvas and then uploads to GPU) - Bitmap Text gives much better performance for dynamically changing text - Text resolution matches the renderer resolution, decrease resolution yourself by setting the `resolution` property, which can consume less memory ### Masks - Masks can be expensive if too many are used: e.g., 100s of masks will really slow things down - Axis-aligned Rectangle masks are the fastest (as they use scissor rect) - Graphics masks are second fastest (as they use the stencil buffer) - Sprite masks are the third fastest (they use filters). They are really expensive. Do not use too many in your scene! ### Filters - Release memory: `container.filters = null` - If you know the size of them: `container.filterArea = new Rectangle(x,y,w,h)`. This can speed things up as it means the object does not need to be measured - Filters are expensive, using too many will start to slow things down! ### BlendModes - Different blend modes will cause batches to break (de-optimize) - ScreenSprite / NormalSprite / ScreenSprite / NormalSprite would be 4 draw calls - ScreenSprite / ScreenSprite / NormalSprite / NormalSprite would be 2 draw calls ### Events - If an object has no interactive children use `interactiveChildren = false`. The event system will then be able to avoid crawling through the object - Setting `hitArea = new Rectangle(x,y,w,h)` as above should stop the event system from crawling through the object --- ## Render Groups # Render Groups ## Understanding RenderGroups in PixiJS As you delve deeper into PixiJS, especially with version 8, you'll encounter a powerful feature known as RenderGroups. Think of RenderGroups as specialized containers within your scene graph that act like mini scene graphs themselves. Here's what you need to know to effectively use Render Groups in your projects: ### What Are Render Groups? Render Groups are essentially containers that PixiJS treats as self-contained scene graphs. When you assign parts of your scene to a Render Group, you're telling PixiJS to manage these objects together as a unit. This management includes monitoring for changes and preparing a set of render instructions specifically for the group. This is a powerful tool for optimizing your rendering process. ### Why Use Render Groups? The main advantage of using Render Groups lies in their optimization capabilities. They allow for certain calculations, like transformations (position, scale, rotation), tint, and alpha adjustments, to be offloaded to the GPU. This means that operations like moving or adjusting the Render Group can be done with minimal CPU impact, making your application more performance-efficient. In practice, you're utilizing Render Groups even without explicit awareness. The root element you pass to the render function in PixiJS is automatically converted into a RenderGroup as this is where its render instructions will be stored. Though you also have the option to explicitly create additional RenderGroups as needed to further optimize your project. This feature is particularly beneficial for: - **Static Content:** For content that doesn't change often, a Render Group can significantly reduce the computational load on the CPU. In this case static refers to the scene graph structure, not that actual values of the PixiJS elements inside it (eg position, scale of things). - **Distinct Scene Parts:** You can separate your scene into logical parts, such as the game world and the HUD (Heads-Up Display). Each part can be optimized individually, leading to overall better performance. ### Examples ```ts const myGameWorld = new Container({ isRenderGroup: true, }); const myHud = new Container({ isRenderGroup: true, }); scene.addChild(myGameWorld, myHud); renderer.render(scene); // this action will actually convert the scene to a render group under the hood ``` Check out the [container example](../../examples/basic/container). ### Best Practices - **Don't Overuse:** While Render Groups are powerful, using too many can actually degrade performance. The goal is to find a balance that optimizes rendering without overwhelming the system with too many separate groups. Make sure to profile when using them. The majority of the time you won't need to use them at all! - **Strategic Grouping:** Consider what parts of your scene change together and which parts remain static. Grouping dynamic elements separately from static elements can lead to performance gains. By understanding and utilizing Render Groups, you can take full advantage of PixiJS's rendering capabilities, making your applications smoother and more efficient. This feature represents a powerful tool in the optimization toolkit offered by PixiJS, enabling developers to create rich, interactive scenes that run smoothly across different devices. --- ## Render Layers # Render Layers The PixiJS Layer API provides a powerful way to control the **rendering order** of objects independently of their **logical parent-child relationships** in the scene graph. With RenderLayers, you can decouple how objects are transformed (via their logical parent) from how they are visually drawn on the screen. Using RenderLayers ensures these elements are visually prioritized while maintaining logical parent-child relationships. Examples include: - A character with a health bar: Ensure the health bar always appears on top of the world, even if the character moves behind an object. - UI elements like score counters or notifications: Keep them visible regardless of the game world’s complexity. - Highlighting Elements in Tutorials: Imagine a tutorial where you need to push back most game elements while highlighting a specific object. RenderLayers can split these visually. The highlighted object can be placed in a foreground layer to be rendered above a push back layer. This guide explains the key concepts, provides practical examples, and highlights common gotchas to help you use the Layer API effectively. --- ### **Key Concepts** 1. **Independent Rendering Order**: - RenderLayers allow control of the draw order independently of the logical hierarchy, ensuring objects are rendered in the desired order. 2. **Logical Parenting Stays Intact**: - Objects maintain transformations (e.g., position, scale, rotation) from their logical parent, even when attached to RenderLayers. 3. **Explicit Object Management**: - Objects must be manually reassigned to a layer after being removed from the scene graph or layer, ensuring deliberate control over rendering. 4. **Dynamic Sorting**: - Within layers, objects can be dynamically reordered using `zIndex` and `sortChildren` for fine-grained control of rendering order. --- ### **Basic API Usage** First lets create two items that we want to render, red guy and blue guy. ```typescript const redGuy = new PIXI.Sprite('red guy'); redGuy.tint = 0xff0000; const blueGuy = new PIXI.Sprite('blue guy'); blueGuy.tint = 0x0000ff; stage.addChild(redGuy, blueGuy); ``` ![alt text](render-layers/image-1.png) Now we know that red guy will be rendered first, then blue guy. Now in this simple example you could get away with just sorting the `zIndex` of the red guy and blue guy to help reorder. But this is a guide about render layers, so lets create one of those. Use `renderLayer.attach` to assign an object to a layer. This overrides the object’s default render order defined by its logical parent. ```typescript // a layer.. const layer = new RenderLayer(); stage.addChild(layer); layer.attach(redGuy); ``` ![alt text](render-layers/image-2.png) So now our scene graph order is: ``` |- stage |-- redGuy |-- blueGuy |-- layer ``` And our render order is: ``` |- stage |-- blueGuy |-- layer |-- redGuy ``` This happens because the layer is now the last child in the stage. Since the red guy is attached to the layer, it will be rendered at the layer's position in the scene graph. However, it still logically remains in the same place in the scene hierarchy. #### **3. Removing Objects from a Layer** Now let's remove the red guy from the layer. To stop an object from being rendered in a layer, use `removeFromLayer`. Once removed from the layer, its still going to be in the scene graph, and will be rendered in its scene graph order. ```typescript layer.detach(redGuy); // Stop rendering the rect via the layer ``` ![alt text](render-layers/image-1.png) Removing an object from its logical parent (`removeChild`) automatically removes it from the layer. ```typescript stage.removeChild(redGuy); // if the red guy was removed from the stage, it will also be removed from the layer ``` ![alt text](render-layers/image-3.png) However, if you remove the red guy from the stage and then add it back to the stage, it will not be added to the layer again. ```typescript // add red guy to his original position stage.addChildAt(redGuy, 0); ``` ![alt text](render-layers/image-1.png) You will need to reattach it to the layer yourself. ```typescript layer.attach(redGuy); // re attach it to the layer again! ``` ![alt text](render-layers/image-2.png) This may seem like a pain, but it's actually a good thing. It means that you have full control over the render order of the object, and you can change it at any time. It also means you can't accidentally add an object to a container and have it automatically re-attach to a layer that may or may not still be around - it would be quite confusing and lead to some very hard to debug bugs! #### **5. Layer Position in Scene Graph** The layer’s position in the scene graph determines its render priority relative to other layers and objects. ```typescript // reparent the layer to render first in the stage stage.addChildAt(layer, 0); ``` ## ![alt text](render-layers/image-1.png) ### **Complete Example** Here’s a real-world example that shows how to use RenderLayers to set ap player ui on top of the world. ```ts import { Application, Assets, Container, DisplacementFilter, RenderLayer, Sprite, TilingSprite, } from 'pixi.js'; import { Fish } from './Fish'; (async () => { // Create a new application const app = new Application(); // Initialize the application await app.init({ width: 630, height: 410, antialias: true }); // Append the application canvas to the document body document.body.appendChild(app.canvas); // move the canvas to the center of the screen app.canvas.style.position = 'absolute'; app.canvas.style.top = `${window.innerHeight / 2 - app.canvas.height / 2}px`; app.canvas.style.left = `${window.innerWidth / 2 - app.canvas.width / 2}px`; // Load textures await Assets.load([ `https://pixijs.com/assets/pond/displacement_BG.jpg`, `https://pixijs.com/assets/pond/overlay.png`, `https://pixijs.com/assets/pond/displacement_map.png`, `https://pixijs.com/assets/pond/displacement_fish1.png`, `https://pixijs.com/assets/pond/displacement_fish2.png`, ]); const background = Sprite.from( 'https://pixijs.com/assets/pond/displacement_BG.jpg', ); const pondContainer = new Container(); pondContainer.addChild(background); app.stage.addChild(pondContainer); const displacementMap = Assets.get( 'https://pixijs.com/assets/pond/displacement_map.png', ); displacementMap.source.wrapMode = 'repeat'; const displacementSprite = Sprite.from(displacementMap); const displacementFilter = new DisplacementFilter(displacementSprite, 40); pondContainer.addChild(displacementSprite); pondContainer.filters = [displacementFilter]; const uiLayer = new RenderLayer(); const fishes = []; const names = [ 'Alice', 'Bob', 'Caroline', 'David', 'Ellie', 'Frank', 'Gloria', 'Henry', 'Isabel', 'Jack', ]; const textures = [ Assets.get('https://pixijs.com/assets/pond/displacement_fish1.png'), Assets.get('https://pixijs.com/assets/pond/displacement_fish2.png'), ]; for (let i = 0; i < 10; i++) { const fish = new Fish( names[i % names.length], textures[i % textures.length], ); fishes.push(fish); pondContainer.addChild(fish); fish.x = Math.random() * 630; fish.y = Math.random() * 410; uiLayer.attach(fish.ui); } const waterOverlay = TilingSprite.from( Assets.get('https://pixijs.com/assets/pond/overlay.png'), ); waterOverlay.width = 630; waterOverlay.height = 410; pondContainer.addChild(waterOverlay); app.stage.addChild(uiLayer); // Animate the mask app.ticker.add(() => { waterOverlay.tilePosition.x += 0.5; waterOverlay.tilePosition.y += 0.5; displacementSprite.x += 0.5; displacementSprite.y += 0.5; fishes.forEach((fish) => fish.update()); }); })(); ``` ```ts import { Container, Sprite } from 'pixi.js'; import { CharacterUI } from './CharacterUI'; export class Fish extends Container { ui; _speed = 1 + Number(Math.random()); _direction = Math.random() * Math.PI * 2; fishView; constructor(name, texture) { super(); this.fishView = new Sprite(texture); this.fishView.anchor.set(0.5); this.addChild(this.fishView); this.ui = new CharacterUI(name, 20); this.ui.y = 0; this.addChild(this.ui); } update() { this._direction += 0.001; this.fishView.rotation = Math.PI - this._direction; this.x += this._speed * Math.cos(-this._direction); this.y += this._speed * Math.sin(-this._direction); // wrap around the screen const padding = 100; const width = 630; const height = 410; if (this.x > width + padding) this.x -= width + padding * 2; if (this.x < -padding) this.x += width + padding * 2; if (this.y > height + padding) this.y -= height + padding * 2; if (this.y < -padding) this.y += height + padding * 2; } } ``` ```ts import { Container, Graphics, Text } from 'pixi.js'; export class CharacterUI extends Container { constructor(name) { super(); const label = new Text({ text: name, resolution: 2, style: { fontSize: 16, fill: 0x000000 }, anchor: 0.5, }); const padding = 10; const bg = new Graphics() .roundRect( -label.width / 2 - padding, -label.height / 2 - padding, label.width + padding * 2, label.height + padding * 2, 20, ) .fill({ color: 0xffff00, alpha: 1, }); this.addChild(bg, label); } } ``` --- ### **Gotchas and Things to Watch Out For** 1. **Manual Reassignment**: - When an object is re-added to a logical parent, it does not automatically reassociate with its previous layer. Always reassign the object to the layer explicitly. 2. **Nested Children**: - If you remove a parent container, all its children are automatically removed from layers. Be cautious with complex hierarchies. 3. **Sorting Within Layers**: - Objects in a layer can be sorted dynamically using their `zIndex` property. This is useful for fine-grained control of render order. ```javascript rect.zIndex = 10; // Higher values render later layer.sortableChildren = true; // Enable sorting layer.sortRenderLayerChildren(); // Apply the sorting ``` 4. **Layer Overlap**: - If multiple layers overlap, their order in the scene graph determines the render priority. Ensure the layering logic aligns with your desired visual output. --- ### **Best Practices** 1. **Group Strategically**: Minimize the number of layers to optimize performance. 2. **Use for Visual Clarity**: Reserve layers for objects that need explicit control over render order. 3. **Test Dynamic Changes**: Verify that adding, removing, or reassigning objects to layers behaves as expected in your specific scene setup. By understanding and leveraging RenderLayers effectively, you can achieve precise control over your scene's visual presentation while maintaining a clean and logical hierarchy. --- ## Render Loop # Render Loop At the core of PixiJS lies its **render loop**, a repeating cycle that updates and redraws your scene every frame. Unlike traditional web development where rendering is event-based (e.g. on user input), PixiJS uses a continuous animation loop that provides full control over real-time rendering. This guide provides a deep dive into how PixiJS structures this loop internally, from the moment a frame begins to when it is rendered to the screen. Understanding this will help you write more performant, well-structured applications. ## Overview Each frame, PixiJS performs the following sequence: 1. **Tickers are executed** (user logic) 2. **Scene graph is updated** (transforms and culling) 3. **Rendering occurs** (GPU draw calls) This cycle repeats as long as your application is running and its ticker is active. ## Step 1: Running Ticker Callbacks The render loop is driven by the `Ticker` class, which uses `requestAnimationFrame` to schedule work. Each tick: - Measures elapsed time since the previous frame - Caps it based on `minFPS` and `maxFPS` - Calls every listener registered with `ticker.add()` or `app.ticker.add()` ### Example ```ts app.ticker.add((ticker) => { bunny.rotation += ticker.deltaTime * 0.1; }); ``` Every callback receives the current `Ticker` instance. You can access `ticker.deltaTime` (scaled frame delta) and `ticker.elapsedMS` (unscaled delta in ms) to time animations. ## Step 2: Updating the Scene Graph PixiJS uses a hierarchical **scene graph** to represent all visual objects. Before rendering, the graph needs to be traversed to: - Recalculate transforms (world matrix updates) - Apply custom logic via `onRender` handlers - Apply culling if enabled ## Step 3: Rendering the Scene Once the scene graph is ready, the renderer walks the display list starting at `app.stage`: 1. Applies global and local transformations 2. Batches draw calls when possible 3. Uploads geometry, textures, and uniforms 4. Issues GPU commands All rendering is **retained mode**: objects persist across frames unless explicitly removed. Rendering is done via either WebGL or WebGPU, depending on your environment. The renderer abstracts away the differences behind a common API. ## Full Frame Lifecycle Diagram ```plaintext requestAnimationFrame │ [Ticker._tick()] │ ├─ Compute elapsed time ├─ Call user listeners │ └─ sprite.onRender ├─ Cull display objects (if enabled) ├─ Update world transforms └─ Render stage ├─ Traverse display list ├─ Upload data to GPU └─ Draw ``` --- ## Scene Graph # Scene Graph Every frame, PixiJS is updating and then rendering the scene graph. Let's talk about what's in the scene graph, and how it impacts how you develop your project. If you've built games before, this should all sound very familiar, but if you're coming from HTML and the DOM, it's worth understanding before we get into specific types of objects you can render. ## The Scene Graph Is a Tree The scene graph's root node is a container maintained by the application, and referenced with `app.stage`. When you add a sprite or other renderable object as a child to the stage, it's added to the scene graph and will be rendered and interactable. PixiJS `Containers` can also have children, and so as you build more complex scenes, you will end up with a tree of parent-child relationships, rooted at the app's stage. (A helpful tool for exploring your project is the [Pixi.js devtools plugin](https://chrome.google.com/webstore/detail/pixijs-devtools/aamddddknhcagpehecnhphigffljadon) for Chrome, which allows you to view and manipulate the scene graph in real time as it's running!) ## Parents and Children When a parent moves, its children move as well. When a parent is rotated, its children are rotated too. Hide a parent, and the children will also be hidden. If you have a game object that's made up of multiple sprites, you can collect them under a container to treat them as a single object in the world, moving and rotating as one. Each frame, PixiJS runs through the scene graph from the root down through all the children to the leaves to calculate each object's final position, rotation, visibility, transparency, etc. If a parent's alpha is set to 0.5 (making it 50% transparent), all its children will start at 50% transparent as well. If a child is then set to 0.5 alpha, it won't be 50% transparent, it will be 0.5 x 0.5 = 0.25 alpha, or 75% transparent. Similarly, an object's position is relative to its parent, so if a parent is set to an x position of 50 pixels, and the child is set to an x position of 100 pixels, it will be drawn at a screen offset of 150 pixels, or 50 + 100. Here's an example. We'll create three sprites, each a child of the last, and animate their position, rotation, scale and alpha. Even though each sprite's properties are set to the same values, the parent-child chain amplifies each change: ```ts import { Application, Assets, Container, Sprite } from 'pixi.js'; (async () => { // Create the application helper and add its render target to the page const app = new Application(); await app.init({ resizeTo: window }); document.body.appendChild(app.canvas); // Add a container to center our sprite stack on the page const container = new Container({ x: app.screen.width / 2, y: app.screen.height / 2, }); app.stage.addChild(container); // load the texture const tex = await Assets.load('https://pixijs.com/assets/bunny.png'); // Create the 3 sprites, each a child of the last const sprites = []; let parent = container; for (let i = 0; i < 3; i++) { const wrapper = new Container(); const sprite = Sprite.from(tex); sprite.anchor.set(0.5); wrapper.addChild(sprite); parent.addChild(wrapper); sprites.push(wrapper); parent = wrapper; } // Set all sprite's properties to the same value, animated over time let elapsed = 0.0; app.ticker.add((delta) => { elapsed += delta.deltaTime / 60; const amount = Math.sin(elapsed); const scale = 1.0 + 0.25 * amount; const alpha = 0.75 + 0.25 * amount; const angle = 40 * amount; const x = 75 * amount; for (let i = 0; i < sprites.length; i++) { const sprite = sprites[i]; sprite.scale.set(scale); sprite.alpha = alpha; sprite.angle = angle; sprite.x = x; } }); })(); ``` The cumulative translation, rotation, scale and skew of any given node in the scene graph is stored in the object's `worldTransform` property. Similarly, the cumulative alpha value is stored in the `worldAlpha` property. ## Render Order So we have a tree of things to draw. Who gets drawn first? PixiJS renders the tree from the root down. At each level, the current object is rendered, then each child is rendered in order of insertion. So the second child is rendered on top of the first child, and the third over the second. Check out this example, with two parent objects A & D, and two children B & C under A: ```ts import { Application, Container, Sprite, Text, Texture } from 'pixi.js'; (async () => { // Create the application helper and add its render target to the page const app = new Application(); await app.init({ resizeTo: window }); document.body.appendChild(app.canvas); // Label showing scene graph hierarchy const label = new Text({ text: 'Scene Graph:\n\napp.stage\n ┗ A\n ┗ B\n ┗ C\n ┗ D', style: { fill: '#ffffff' }, position: { x: 300, y: 100 }, }); app.stage.addChild(label); // Helper function to create a block of color with a letter const letters = []; function addLetter(letter, parent, color, pos) { const bg = new Sprite(Texture.WHITE); bg.width = 100; bg.height = 100; bg.tint = color; const text = new Text({ text: letter, style: { fill: '#ffffff' }, }); text.anchor.set(0.5); text.position = { x: 50, y: 50 }; const container = new Container(); container.position = pos; container.visible = false; container.addChild(bg, text); parent.addChild(container); letters.push(container); return container; } // Define 4 letters const a = addLetter('A', app.stage, 0xff0000, { x: 100, y: 100 }); const b = addLetter('B', a, 0x00ff00, { x: 20, y: 20 }); const c = addLetter('C', a, 0x0000ff, { x: 20, y: 40 }); const d = addLetter('D', app.stage, 0xff8800, { x: 140, y: 100 }); // Display them over time, in order let elapsed = 0.0; app.ticker.add((ticker) => { elapsed += ticker.deltaTime / 60.0; if (elapsed >= letters.length) { elapsed = 0.0; } for (let i = 0; i < letters.length; i++) { letters[i].visible = elapsed >= i; } }); })(); ``` If you'd like to re-order a child object, you can use `setChildIndex()`. To add a child at a given point in a parent's list, use `addChildAt()`. Finally, you can enable automatic sorting of an object's children using the `sortableChildren` option combined with setting the `zIndex` property on each child. ## RenderGroups As you delve deeper into PixiJS, you'll encounter a powerful feature known as Render Groups. Think of Render Groups as specialized containers within your scene graph that act like mini scene graphs themselves. Here's what you need to know to effectively use Render Groups in your projects. For more info check out the [RenderGroups overview](./render-groups.md) ## Culling If you're building a project where a large proportion of your scene objects are off-screen (say, a side-scrolling game), you will want to _cull_ those objects. Culling is the process of evaluating if an object (or its children!) is on the screen, and if not, turning off rendering for it. If you don't cull off-screen objects, the renderer will still draw them, even though none of their pixels end up on the screen. PixiJS provides built-in support for viewport culling. To enable culling, set `cullable = true` on your objects. You can also set `cullableChildren` to `false` to allow PixiJS to bypass the recursive culling function, which can improve performance. Additionally, you can set `cullArea` to further optimize performance by defining the area to be culled. ## Local vs Global Coordinates If you add a sprite to the stage, by default it will show up in the top left corner of the screen. That's the origin of the global coordinate space used by PixiJS. If all your objects were children of the stage, that's the only coordinates you'd need to worry about. But once you introduce containers and children, things get more complicated. A child object at [50, 100] is 50 pixels right and 100 pixels down _from its parent_. We call these two coordinate systems "global" and "local" coordinates. When you use `position.set(x, y)` on an object, you're always working in local coordinates, relative to the object's parent. The problem is, there are many times when you want to know the global position of an object. For example, if you want to cull offscreen objects to save render time, you need to know if a given child is outside the view rectangle. To convert from local to global coordinates, you use the `toGlobal()` function. Here's a sample usage: ```javascript // Get the global position of an object, relative to the top-left of the screen let globalPos = obj.toGlobal(new Point(0, 0)); ``` This snippet will set `globalPos` to be the global coordinates for the child object, relative to [0, 0] in the global coordinate system. ## Global vs Screen Coordinates When your project is working with the host operating system or browser, there is a third coordinate system that comes into play - "screen" coordinates (aka "viewport" coordinates). Screen coordinates represent position relative to the top-left of the canvas element that PixiJS is rendering into. Things like the DOM and native mouse click events work in screen space. Now, in many cases, screen space is equivalent to world space. This is the case if the size of the canvas is the same as the size of the render view specified when you create you `Application`. By default, this will be the case - you'll create for example an 800x600 application window and add it to your HTML page, and it will stay that size. 100 pixels in world coordinates will equal 100 pixels in screen space. BUT! It is common to stretch the rendered view to have it fill the screen, or to render at a lower resolution and up-scale for speed. In that case, the screen size of the canvas element will change (e.g. via CSS), but the underlying render view will _not_, resulting in a mis-match between world coordinates and screen coordinates. --- ## Accessibility # Accessibility PixiJS includes built-in accessibility support through a DOM-based overlay system that integrates with screen readers, keyboard navigation, and other assistive technologies. It uses `` overlays to describe visual elements to screen readers :::info Accessibility is opt-in to reduce bundle size and must be explicitly enabled. ::: ```ts import 'pixi.js/accessibility'; import { Container } from 'pixi.js'; const button = new Container(); button.accessible = true; ``` ## **How It Works** PixiJS places DOM `` elements over your canvas, aligned to the bounds of accessible objects. These elements: - Can receive focus via keyboard (`tabIndex`) - Announce `accessibleTitle` or `accessibleHint` to screen readers - Dispatch `click`, `mouseover`, `mouseout` events as Pixi pointer events - Use `aria-live` and `aria-label` where appropriate ## Enabling the System To enable accessibility, you must import the module before creating your renderer: ```ts import 'pixi.js/accessibility'; ``` PixiJS automatically installs the `AccessibilitySystem` onto your renderer. You can configure how and when it's activated. ## **Configuration Options** You can customize when and how the accessibility system activates by passing options to the `Application` constructor: ```ts const app = new Application({ accessibilityOptions: { enabledByDefault: true, // Enable on startup activateOnTab: false, // Disable auto-activation via tab deactivateOnMouseMove: false, // Keep system active with mouse use debug: true, // Show div overlays for debugging }, }); ``` Or programmatically enable/disable the system: ```ts app.renderer.accessibility.setAccessibilityEnabled(true); ``` ## **Creating Accessible Objects** To mark a display object as accessible and add it to the accessibility system, set the `accessible` property to `true`. This will create a `` overlay that screen readers can interact with. ```ts const button = new Container(); button.accessible = true; app.stage.addChild(button); ``` ### **Properties for Accessible Containers** There are several properties you can set on accessible containers to customize their behavior: | Property | Description | | ------------------------- | ---------------------------------------------------------------- | | `accessible` | Enables accessibility for the object | | `accessibleTitle` | Sets the `title` for screen readers | | `accessibleHint` | Sets the `aria-label` | | `accessibleText` | Alternative inner text for the div | | `accessibleType` | Tag name used for the shadow element (`'button'`, `'div'`, etc.) | | `accessiblePointerEvents` | CSS `pointer-events` value (`'auto'`, `'none'`, etc.) | | `tabIndex` | Allows focus with keyboard navigation | | `accessibleChildren` | Whether children of this container are accessible | --- ## **API Reference** - [Overview](https://pixijs.download/release/docs/accessibility.html) - [AccessibilitySystem](https://pixijs.download/release/docs/accessibility.AccessibilitySystem.html) - [AccessibleOptions](https://pixijs.download/release/docs/accessibility.AccessibleOptions.html) --- ## Color # Color The `Color` class in PixiJS is a flexible utility for representing colors. It is used throughout the rendering pipeline for things like tints, fills, strokes, gradients, and more. ```ts import { Color, Sprite, Texture, Graphics } from 'pixi.js'; const red = new Color('red'); // Named color const green = new Color(0x00ff00); // Hex const blue = new Color('#0000ff'); // Hex string const rgba = new Color({ r: 255, g: 0, b: 0, a: 0.5 }); // RGBA object console.log(red.toArray()); // [1, 0, 0, 1] console.log(green.toHex()); // "#00ff00" const sprite = new Sprite(Texture.WHITE); sprite.tint = red; // Works directly with a Color instance ``` ## Using `Color` and `ColorSource` PixiJS supports many color formats through the `ColorSource` type: - Color names: `'red'`, `'white'`, `'blue'`, etc. - Hex integers: `0xffcc00` - Hex strings: `'ffcc00'`, `'#f00'`, `'0xffcc00ff'` - RGB(A) objects: `{ r: 255, g: 0, b: 0 }`, `{ r: 255, g: 0, b: 0, a: 0.5 }` - RGB(A) strings: `'rgb(255,0,0)'`, `'rgba(255,0,0,0.5)'` - RGB(A) arrays: `[1, 0, 0]`, `[1, 0, 0, 0.5]` - Typed arrays: `Uint8Array`, `Float32Array` - HSL/HSV objects and strings - `Color` instances Whenever you see a color-related property (e.g., `fill`, `tint`, `stroke`), you can use any of these formats. The library will automatically convert them to the appropriate format internally. ```ts import { Graphics, Sprite, Texture } from 'pixi.js'; const sprite = new Sprite(Texture.WHITE); sprite.tint = 'red'; // converted internally const graphics = new Graphics(); graphics.fill({ color: '#00ff00' }); // Also converted internally ``` --- ## API Reference - [Color](https://pixijs.download/release/docs/color.Color.html) --- ## Events / Interaction # Events / Interaction PixiJS is primarily a rendering library, but it provides a flexible and performant event system designed for both mouse and touch input. This system replaces the legacy `InteractionManager` from previous versions with a unified, DOM-like federated event model. ```ts const sprite = new Sprite(texture); sprite.eventMode = 'static'; sprite.on('pointerdown', () => { console.log('Sprite clicked!'); }); ``` ## Event Modes To use the event system, set the `eventMode` of a `Container` (or its subclasses like `Sprite`) and subscribe to event listeners. The `eventMode` property controls how an object interacts with the event system: | Mode | Description | | --------- | ------------------------------------------------------------------------------------------------------------------------ | | `none` | Ignores all interaction events, including children. Optimized for non-interactive elements. | | `passive` | _(default)_ Ignores self-hit testing and does not emit events, but interactive children still receive events. | | `auto` | Participates in hit testing only if a parent is interactive. Does not emit events. | | `static` | Emits events and is hit tested. Suitable for non-moving interactive elements like buttons. | | `dynamic` | Same as `static`, but also receives synthetic events when the pointer is idle. Suitable for animating or moving targets. | ## Event Types PixiJS supports a rich set of DOM-like event types across mouse, touch, and pointer input. Below is a categorized list. ### Pointer Events (Recommended for general use) | Event Type | Description | | ------------------- | ---------------------------------------------------------------------------------- | | `pointerdown` | Fired when a pointer (mouse, pen, or touch) is pressed on a display object. | | `pointerup` | Fired when the pointer is released over the display object. | | `pointerupoutside` | Fired when the pointer is released outside the object that received `pointerdown`. | | `pointermove` | Fired when the pointer moves over the display object. | | `pointerover` | Fired when the pointer enters the boundary of the display object. | | `pointerout` | Fired when the pointer leaves the boundary of the display object. | | `pointerenter` | Fired when the pointer enters the display object (does not bubble). | | `pointerleave` | Fired when the pointer leaves the display object (does not bubble). | | `pointercancel` | Fired when the pointer interaction is canceled (e.g. touch lost). | | `pointertap` | Fired when a pointer performs a quick tap. | | `globalpointermove` | Fired on every pointer move, regardless of whether any display object is hit. | ### Mouse Events (Used for mouse-specific input) | Event Type | Description | | ----------------- | ------------------------------------------------------------------------------------------- | | `mousedown` | Fired when a mouse button is pressed on a display object. | | `mouseup` | Fired when a mouse button is released over the object. | | `mouseupoutside` | Fired when a mouse button is released outside the object that received `mousedown`. | | `mousemove` | Fired when the mouse moves over the display object. | | `mouseover` | Fired when the mouse enters the display object. | | `mouseout` | Fired when the mouse leaves the display object. | | `mouseenter` | Fired when the mouse enters the object, does not bubble. | | `mouseleave` | Fired when the mouse leaves the object, does not bubble. | | `click` | Fired when a mouse click (press and release) occurs on the object. | | `rightdown` | Fired when the right mouse button is pressed on the display object. | | `rightup` | Fired when the right mouse button is released over the object. | | `rightupoutside` | Fired when the right mouse button is released outside the object that received `rightdown`. | | `rightclick` | Fired when a right mouse click (press and release) occurs on the object. | | `globalmousemove` | Fired on every mouse move, regardless of display object hit. | | `wheel` | Fired when the mouse wheel is scrolled while over the display object. | ### Touch Events | Event Type | Description | | ----------------- | ------------------------------------------------------------------------------------- | | `touchstart` | Fired when a new touch point is placed on a display object. | | `touchend` | Fired when a touch point is lifted from the display object. | | `touchendoutside` | Fired when a touch point ends outside the object that received `touchstart`. | | `touchmove` | Fired when a touch point moves across the display object. | | `touchcancel` | Fired when a touch interaction is canceled (e.g. device gesture). | | `tap` | Fired when a touch point taps the display object. | | `globaltouchmove` | Fired on every touch move, regardless of whether a display object is under the touch. | ### Global Events In previous versions of PixiJS, events such as `pointermove`, `mousemove`, and `touchmove` were fired when any move event was captured by the canvas, even if the pointer was not over a display object. This behavior changed in v8 and now these events are fired only when the pointer is over a display object. To maintain the old behavior, you can use the `globalpointermove`, `globalmousemove`, and `globaltouchmove` events. These events are fired on every pointer/touch move, regardless of whether any display object is hit. ```ts const sprite = new Sprite(texture); sprite.eventMode = 'static'; sprite.on('globalpointermove', (event) => { console.log('Pointer moved globally!', event); }); ``` ## How Hit Testing Works When an input event occurs (mouse move, click, etc.), PixiJS walks the display tree to find the top-most interactive element under the pointer: - If `interactiveChildren` is `false` on a `Container`, its children will be skipped. - If a `hitArea` is set, it overrides bounds-based hit testing. - If `eventMode` is `'none'`, the element and its children are skipped. Once the top-most interactive element is found, the event is dispatched to it. If the event bubbles, it will propagate up the display tree. If the event is not handled, it will continue to bubble up to parent containers until it reaches the root. ### Custom Hit Area Custom hit areas can be defined using the `hitArea` property. This property can be set on any scene object, including `Sprite`, `Container`, and `Graphics`. Using a custom hit area allows you to define a specific area for interaction, which can be different from the object's bounding box. It also can improve performance by reducing the number of objects that need to be checked for interaction. ```ts import { Rectangle, Sprite } from 'pixi.js'; const sprite = new Sprite(texture); sprite.hitArea = new Rectangle(0, 0, 100, 100); sprite.eventMode = 'static'; ``` ## Listening to Events PixiJS supports both `on()`/`off()` and `addEventListener()`/`removeEventListener()` and event callbacks (`onclick: ()=> {}`) for adding and removing event listeners. The `on()` method is recommended for most use cases as it provides a more consistent API across different event types used throughout PixiJS. ### Using `on()` (from EventEmitter) ```ts const eventFn = (e) => console.log('clicked'); sprite.on('pointerdown', eventFn); sprite.once('pointerdown', eventFn); sprite.off('pointerdown', eventFn); ``` ### Using DOM-style Events ```ts sprite.addEventListener( 'click', (event) => { console.log('Clicked!', event.detail); }, { once: true }, ); ``` ### Using callbacks ```ts sprite.onclick = (event) => { console.log('Clicked!', event.detail); }; ``` ## Checking for Interactivity You can check if a `Sprite` or `Container` is interactive by using the `isInteractive()` method. This method returns `true` if the object is interactive and can receive events. ```ts if (sprite.isInteractive()) { // true if eventMode is static or dynamic } ``` ## Custom Cursors PixiJS allows you to set a custom cursor for interactive objects using the `cursor` property. This property accepts a string representing the CSS cursor type. ```ts const sprite = new Sprite(texture); sprite.eventMode = 'static'; sprite.cursor = 'pointer'; // Set the cursor to a pointer when hovering over the sprite ``` ```ts const sprite = new Sprite(texture); sprite.eventMode = 'static'; sprite.cursor = 'url(my-cursor.png), auto'; // Set a custom cursor image ``` ### Default Custom Cursors You can also set default values to be used for all interactive objects. ```ts // CSS style for icons const defaultIcon = "url('https://pixijs.com/assets/bunny.png'),auto"; const hoverIcon = "url('https://pixijs.com/assets/bunny_saturated.png'),auto"; // Add custom cursor styles app.renderer.events.cursorStyles.default = defaultIcon; app.renderer.events.cursorStyles.hover = hoverIcon; const sprite = new Sprite(texture); sprite.eventMode = 'static'; sprite.cursor = 'hover'; ``` --- ## API Reference - [Overview](https://pixijs.download/release/docs/events.html) - [EventSystem](https://pixijs.download/release/docs/events.EventSystem.html) - [Cursor](https://pixijs.download/release/docs/events.html#Cursor) - [EventMode](https://pixijs.download/release/docs/events.html#EventMode) - [Container](https://pixijs.download/release/docs/scene.Container.html) - [FederatedEvent](https://pixijs.download/release/docs/events.FederatedEvent.html) - [FederatedMouseEvent](https://pixijs.download/release/docs/events.FederatedMouseEvent.html) - [FederatedWheelEvent](https://pixijs.download/release/docs/events.FederatedWheelEvent.html) - [FederatedPointerEvent](https://pixijs.download/release/docs/events.FederatedPointerEvent.html) --- ## Filters / Blend Modes # Filters / Blend Modes PixiJS filters allow you to apply post-processing visual effects to any scene object and its children. Filters can be used for effects such as blurring, color adjustments, noise, or custom shader-based operations. ```ts import { Sprite, BlurFilter } from 'pixi.js'; // Apply the filter sprite.filters = [new BlurFilter({ strength: 8 })]; ``` --- ## Applying Filters Applying filters is straightforward. You can assign a filter instance to the `filters` property of any scene object, such as `Sprite`, `Container`, or `Graphics`. You can apply multiple filters by passing an array of filter instances. ```ts import { BlurFilter, NoiseFilter } from 'pixi.js'; sprite.filters = new BlurFilter({ strength: 5 }); sprite.filters = [ new BlurFilter({ strength: 4 }), new NoiseFilter({ noise: 0.2 }), ]; ``` :::info Order matters — filters are applied in sequence. ::: --- ## Advanced Blend Modes PixiJS v8 introduces advanced blend modes for filters, allowing for more complex compositing effects. These blend modes can be used to create unique visual styles and effects. To use advanced modes like `HARD_LIGHT`, you must manually import the advanced blend mode extension: ```ts import 'pixi.js/advanced-blend-modes'; import { HardMixBlend } from 'pixi.js'; sprite.filters = [new HardMixBlend()]; ``` --- ## Built-In Filters Overview PixiJS v8 provides a variety of filters out of the box: | Filter Class | Description | | -------------------- | ------------------------------------------- | | `AlphaFilter` | Applies transparency to an object. | | `BlurFilter` | Gaussian blur. | | `ColorMatrixFilter` | Applies color transformations via a matrix. | | `DisplacementFilter` | Distorts an object using another texture. | | `NoiseFilter` | Adds random noise for a grainy effect. | :::info To explore more community filters, see [pixi-filters](https://pixijs.io/filters/docs/). ::: **Blend Filters**: Used for custom compositing modes | Filter Class | Description | | ------------------ | -------------------------------------------------- | | `ColorBurnBlend` | Darkens the base color to reflect the blend color. | | `ColorDodgeBlend` | Brightens the base color. | | `DarkenBlend` | Retains the darkest color components. | | `DivideBlend` | Divides the base color by the blend color. | | `HardMixBlend` | High-contrast blend. | | `LinearBurnBlend` | Darkens using linear formula. | | `LinearDodgeBlend` | Lightens using linear formula. | | `LinearLightBlend` | Combination of linear dodge and burn. | | `PinLightBlend` | Selective replacement of colors. | | `SubtractBlend` | Subtracts the blend color from base. | --- ## Creating a Custom Filter To define a custom filter in PixiJS v8, you use `Filter.from()` with shader programs and GPU resources. ```ts import { Filter, GlProgram, Texture } from 'pixi.js'; const vertex = ` in vec2 aPosition; out vec2 vTextureCoord; uniform vec4 uInputSize; uniform vec4 uOutputFrame; uniform vec4 uOutputTexture; vec4 filterVertexPosition( void ) { vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy; position.x = position.x * (2.0 / uOutputTexture.x) - 1.0; position.y = position.y * (2.0*uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z; return vec4(position, 0.0, 1.0); } vec2 filterTextureCoord( void ) { return aPosition * (uOutputFrame.zw * uInputSize.zw); } void main(void) { gl_Position = filterVertexPosition(); vTextureCoord = filterTextureCoord(); } `; const fragment = ` in vec2 vTextureCoord; in vec4 vColor; uniform sampler2D uTexture; uniform float uTime; void main(void) { vec2 uvs = vTextureCoord.xy; vec4 fg = texture2D(uTexture, vTextureCoord); fg.r = uvs.y + sin(uTime); gl_FragColor = fg; } `; const customFilter = new Filter({ glProgram: new GlProgram({ fragment, vertex, }), resources: { timeUniforms: { uTime: { value: 0.0, type: 'f32' }, }, }, }); // Apply the filter sprite.filters = [customFilter]; // Update uniform app.ticker.add((ticker) => { filter.resources.timeUniforms.uniforms.uTime += 0.04 * ticker.deltaTime; }); ``` :::info **Tip** Shaders must be WebGL- or WebGPU-compatible. For dual-renderer support, include a `gpuProgram`. ::: --- ## API Reference - [Overview](https://pixijs.download/release/docs/filters.html) - [Filter](https://pixijs.download/release/docs/filters.Filter.html) --- ## Math # Math PixiJS includes a several math utilities for 2D transformations, geometry, and shape manipulation. This guide introduces the most important classes and their use cases, including optional advanced methods enabled via `math-extras`. ## Matrix The `Matrix` class represents a 2D affine transformation matrix. It is used extensively for transformations such as scaling, translation, and rotation. ```ts import { Matrix, Point } from 'pixi.js'; const matrix = new Matrix(); matrix.translate(10, 20).scale(2, 2); const point = new Point(5, 5); const result = matrix.apply(point); // result is (20, 30) ``` --- ## Point and ObservablePoint ### `Point` The Point object represents a location in a two-dimensional coordinate system, where `x` represents the position on the horizontal axis and `y` represents the position on the vertical axis. Many Pixi functions accept the `PointData` type as an alternative to `Point`, which only requires `x` and `y` properties. ```ts import { Point } from 'pixi.js'; const point = new Point(5, 10); point.set(20, 30); // set x and y ``` ### `ObservablePoint` Extends `Point` and triggers a callback when its values change. Used internally for reactive systems like position and scale updates. ```ts import { Point, ObservablePoint } from 'pixi.js'; const observer = { _onUpdate: (point) => { console.log(`Point updated to: (${point.x}, ${point.y})`); }, }; const reactive = new ObservablePoint(observer, 1, 2); reactive.set(3, 4); // triggers call to _onUpdate ``` --- ## Shapes PixiJS includes several 2D shapes, used for hit testing, rendering, and geometry computations. ### `Rectangle` Axis-aligned rectangle defined by `x`, `y`, `width`, and `height`. ```ts import { Rectangle } from 'pixi.js'; const rect = new Rectangle(10, 10, 100, 50); rect.contains(20, 20); // true ``` ### `Circle` Defined by `x`, `y` (center) and `radius`. ```ts import { Circle } from 'pixi.js'; const circle = new Circle(50, 50, 25); circle.contains(50, 75); // true ``` ### `Ellipse` Similar to `Circle`, but supports different width and height (radii). ```ts import { Ellipse } from 'pixi.js'; const ellipse = new Ellipse(0, 0, 20, 10); ellipse.contains(5, 0); // true ``` ### `Polygon` Defined by a list of points. Used for complex shapes and hit testing. ```ts import { Polygon } from 'pixi.js'; const polygon = new Polygon([0, 0, 100, 0, 100, 100, 0, 100]); polygon.contains(50, 50); // true ``` ### `RoundedRectangle` Rectangle with rounded corners, defined by a radius. ```ts import { RoundedRectangle } from 'pixi.js'; const roundRect = new RoundedRectangle(0, 0, 100, 100, 10); roundRect.contains(10, 10); // true ``` ### `Triangle` A convenience wrapper for defining triangles with three points. ```ts import { Triangle } from 'pixi.js'; const triangle = new Triangle(0, 0, 100, 0, 50, 100); triangle.contains(50, 50); // true ``` --- ## Optional: `math-extras` Importing `pixi.js/math-extras` extends `Point` and `Rectangle` with additional vector and geometry utilities. ### To enable: ```ts import 'pixi.js/math-extras'; ``` ### Enhanced `Point` Methods | Method | Description | | ------------------------------- | ------------------------------------------------------------ | | `add(other[, out])` | Adds another point to this one. | | `subtract(other[, out])` | Subtracts another point from this one. | | `multiply(other[, out])` | Multiplies this point with another point component-wise. | | `multiplyScalar(scalar[, out])` | Multiplies the point by a scalar. | | `dot(other)` | Computes the dot product of two vectors. | | `cross(other)` | Computes the scalar z-component of the 3D cross product. | | `normalize([out])` | Returns a normalized (unit-length) vector. | | `magnitude()` | Returns the Euclidean length. | | `magnitudeSquared()` | Returns the squared length (more efficient for comparisons). | | `project(onto[, out])` | Projects this point onto another vector. | | `reflect(normal[, out])` | Reflects the point across a given normal. | ### Enhanced `Rectangle` Methods | Method | Description | | ---------------------------- | ----------------------------------------------------- | | `containsRect(other)` | Returns true if this rectangle contains the other. | | `equals(other)` | Checks if all properties are equal. | | `intersection(other[, out])` | Returns a new rectangle representing the overlap. | | `union(other[, out])` | Returns a rectangle that encompasses both rectangles. | --- ## API Reference - [Overview](https://pixijs.download/release/docs/maths.html) - [Matrix](https://pixijs.download/release/docs/maths.Matrix.html) - [Point](https://pixijs.download/release/docs/maths.Point.html) - [ObservablePoint](https://pixijs.download/release/docs/maths.ObservablePoint.html) - [Rectangle](https://pixijs.download/release/docs/maths.Rectangle.html) - [Circle](https://pixijs.download/release/docs/maths.Circle.html) - [Ellipse](https://pixijs.download/release/docs/maths.Ellipse.html) - [Polygon](https://pixijs.download/release/docs/maths.Polygon.html) - [RoundedRectangle](https://pixijs.download/release/docs/maths.RoundedRectangle.html) - [Triangle](https://pixijs.download/release/docs/maths.Triangle.html) --- ## Textures # Textures Textures are one of the most essential components in the PixiJS rendering pipeline. They define the visual content used by Sprites, Meshes, and other renderable objects. This guide covers how textures are loaded, created, and used, along with the various types of data sources PixiJS supports. ## Texture Lifecycle The texture system is built around two major classes: - **`TextureSource`**: Represents a pixel source, such as an image, canvas, or video. - **`Texture`**: Defines a view into a `TextureSource`, including sub-rectangles, trims, and transformations. ### Lifecycle Flow ``` Source File/Image -> TextureSource -> Texture -> Sprite (or other display object) ``` ### Loading Textures Textures can be loaded asynchronously using the `Assets` system: ```ts const texture = await Assets.load('myTexture.png'); const sprite = new Sprite(texture); ``` ### Preparing Textures Even after you've loaded your textures, the images still need to be pushed to the GPU and decoded. Doing this for a large number of source images can be slow and cause lag spikes when your project first loads. To solve this, you can use the [Prepare](https://pixijs.download/release/docs/rendering.PrepareSystem.html) plugin, which allows you to pre-load textures in a final step before displaying your project. ## Texture vs. TextureSource The `TextureSource` handles the raw pixel data and GPU upload. A `Texture` is a lightweight view on that source, with metadata such as trimming, frame rectangle, UV mapping, etc. Multiple `Texture` instances can share a single `TextureSource`, such as in a sprite sheet. ```ts const sheet = await Assets.load('spritesheet.json'); const heroTexture = sheet.textures['hero.png']; ``` ## Texture Creation You can manually create textures using the constructor: ```ts const mySource = new TextureSource({ resource: myImage }); const texture = new Texture({ source: mySource }); ``` Set `dynamic: true` in the `Texture` options if you plan to modify its `frame`, `trim`, or `source` at runtime. ## Destroying Textures Once you're done with a Texture, you may wish to free up the memory (both WebGL-managed buffers and browser-based) that it uses. To do so, you should call `Assets.unload('texture.png')`, or `texture.destroy()` if you have created the texture outside of Assets. This is a particularly good idea for short-lived imagery like cut-scenes that are large and will only be used once. If a texture is destroyed that was loaded via `Assets` then the assets class will automatically remove it from the cache for you. ## Unload Texture from GPU If you want to unload a texture from the GPU but keep it in memory, you can call `texture.source.unload()`. This will remove the texture from the GPU but keep the source in memory. ```ts // Load the texture const texture = await Assets.load('myTexture.png'); // ... Use the texture // Unload the texture from the GPU texture.source.unload(); ``` ## Texture Types PixiJS supports multiple `TextureSource` types, depending on the kind of input data: | Texture Type | Description | | --------------------- | ----------------------------------------------------------------- | | **ImageSource** | HTMLImageElement, ImageBitmap, SVG's, VideoFrame, etc. | | **CanvasSource** | HTMLCanvasElement or OffscreenCanvas | | **VideoSource** | HTMLVideoElement with optional auto-play and update FPS | | **BufferImageSource** | TypedArray or ArrayBuffer with explicit width, height, and format | | **CompressedSource** | Array of compressed mipmaps (Uint8Array\[]) | ## Common Texture Properties Here are some important properties of the `Texture` class: - `frame`: Rectangle defining the visible portion within the source. - `orig`: Original untrimmed dimensions. - `trim`: Defines trimmed regions to exclude transparent space. - `uvs`: UV coordinates generated from `frame` and `rotate`. - `rotate`: GroupD8 rotation value for atlas compatibility. - `defaultAnchor`: Default anchor when used in Sprites. - `defaultBorders`: Used for 9-slice scaling. - `source`: The `TextureSource` instance. ## Common TextureSource Properties Here are some important properties of the `TextureSource` class: - `resolution`: Affects render size relative to actual pixel size. - `format`: Texture format (e.g., `rgba8unorm`, `bgra8unorm`, etc.) - `alphaMode`: Controls how alpha is interpreted on upload. - `wrapMode` / `scaleMode`: Controls how texture is sampled outside of bounds or when scaled. - `autoGenerateMipmaps`: Whether to generate mipmaps on upload. You can set these properties when creating a `TextureSource`: ```ts texture.source.scaleMode = 'linear'; texture.source.wrapMode = 'repeat'; ``` --- ## API Reference - [Texture](https://pixijs.download/release/docs/rendering.Texture.html) - [TextureSource](https://pixijs.download/release/docs/rendering.TextureSource.html) - [TextureStyle](https://pixijs.download/release/docs/rendering.TextureStyle.html) - [RenderTexture](https://pixijs.download/release/docs/rendering.RenderTexture.html) --- ## Ticker # Ticker The `Ticker` class in PixiJS provides a powerful and flexible mechanism for executing callbacks on every animation frame. It's useful for managing game loops, animations, and any time-based updates. ```ts import { Ticker } from 'pixi.js'; const ticker = new Ticker(); ticker.add((ticker) => { console.log(`Delta Time: ${ticker.deltaTime}`); }); // Start the ticker ticker.start(); ``` ## Adding and Removing Listeners The `Ticker` class allows you to add multiple listeners that will be called on every frame. You can also specify a context for the callback, which is useful for maintaining the correct `this` reference. ```ts ticker.add(myFunction, myContext); ticker.addOnce(myFunction, myContext); ticker.remove(myFunction, myContext); ``` ## Controlling the Ticker ```ts ticker.start(); // Begin calling listeners every frame ticker.stop(); // Pause the ticker and cancel the animation frame ``` To automatically start the ticker when a listener is added, enable `autoStart`: ```ts ticker.autoStart = true; ``` ## Prioritizing Listeners Listeners can be assigned a priority. Higher values run earlier. ```ts import { UPDATE_PRIORITY } from 'pixi.js'; ticker.add(fnA, null, UPDATE_PRIORITY.HIGH); // runs before... ticker.add(fnB, null, UPDATE_PRIORITY.NORMAL); // ...this ``` Available constants include: - `UPDATE_PRIORITY.HIGH = 50` - `UPDATE_PRIORITY.NORMAL = 0` - `UPDATE_PRIORITY.LOW = -50` ## Configuring FPS Tickers allows FPS limits to control the update rate. ### `minFPS` Caps how _slow_ frames are allowed to be. Used to clamp `deltaTime`: ```ts ticker.minFPS = 30; // deltaTime will never act as if below 30 FPS ``` ### `maxFPS` Limits how _fast_ the ticker runs. Useful for conserving CPU/GPU: ```ts ticker.maxFPS = 60; // will not tick faster than 60fps ``` Set to `0` to allow unlimited framerate: ```ts ticker.maxFPS = 0; ``` --- ## API Reference - [Ticker](https://pixijs.download/release/docs/ticker.Ticker.html) - [Application](https://pixijs.download/release/docs/app.Application.html) --- ## Mixing PixiJS and Three.js # Mixing PixiJS and Three.js In many projects, developers aim to harness the strengths of both 3D and 2D graphics. Combining the advanced 3D rendering capabilities of Three.js with the speed and versatility of PixiJS for 2D can result in a powerful, seamless experience. Together, these technologies create opportunities for dynamic and visually compelling applications. Lets see how to do this. :::info NOTE This guide assumes PixiJS will be used as the top layer to deliver UI over a 3D scene rendered by Three.js. However, developers can render either in any order, as many times as needed. This flexibility allows for creative and dynamic applications. ::: --- ### What You’ll Learn - Setting up PixiJS and Three.js to share a single WebGL context. - Using `resetState` to manage renderer states. - Avoiding common pitfalls when working with multiple renderers. --- ### Setting Up #### Step 1: Initialize Three.js Renderer and Scene Three.js will handle the 3D rendering the creation of the dom element and context. ```javascript const WIDTH = window.innerWidth; const HEIGHT = window.innerHeight; const threeRenderer = new THREE.WebGLRenderer({ antialias: true, stencil: true, // so masks work in pixijs }); threeRenderer.setSize(WIDTH, HEIGHT); threeRenderer.setClearColor(0xdddddd, 1); document.body.appendChild(threeRenderer.domElement); const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(70, WIDTH / HEIGHT); camera.position.z = 50; scene.add(camera); const boxGeometry = new THREE.BoxGeometry(10, 10, 10); const basicMaterial = new THREE.MeshBasicMaterial({ color: 0x0095dd }); const cube = new THREE.Mesh(boxGeometry, basicMaterial); cube.rotation.set(0.4, 0.2, 0); scene.add(cube); ``` :::info NOTE We used the dom element and context created by the three.js renderer to pass to the pixijs renderer. This was the simplest way to ensure that the two renderers were using the same WebGL context. You could have done it the other way round if you wanted to. ::: #### Step 2: Initialize PixiJS Renderer and Stage PixiJS will handle the 2D overlay. ```javascript const pixiRenderer = new PIXI.WebGLRenderer(); await pixiRenderer.init({ context: threeRenderer.getContext(), width: WIDTH, height: HEIGHT, clearBeforeRender: false, // Prevent PixiJS from clearing the Three.js render }); const stage = new PIXI.Container(); const amazingUI = new PIXI.Graphics() .roundRect(20, 80, 100, 100, 5) .roundRect(220, 80, 100, 100, 5) .fill(0xffff00); stage.addChild(amazingUI); ``` --- ### Rendering Loop To ensure smooth transitions between the renderers, reset their states before each render: ```javascript function render() { // Render the Three.js scene threeRenderer.resetState(); threeRenderer.render(scene, camera); // Render the PixiJS stage pixiRenderer.resetState(); pixiRenderer.render({ container: stage }); requestAnimationFrame(render); } requestAnimationFrame(render); ``` --- ### Example: Combining 3D and 2D Elements Here’s the complete example integrating PixiJS and Three.js: ```ts // Import required classes from PixiJS and Three.js import { Container, Graphics, Text, WebGLRenderer } from 'pixi.js'; import * as THREE from 'three'; // Self-executing async function to set up the demo (async () => { // Initialize window dimensions let WIDTH = window.innerWidth; let HEIGHT = window.innerHeight; // === THREE.JS SETUP === // Create Three.js WebGL renderer with antialiasing and stencil buffer const threeRenderer = new THREE.WebGLRenderer({ antialias: true, stencil: true, }); // Configure Three.js renderer size and background color threeRenderer.setSize(WIDTH, HEIGHT); threeRenderer.setClearColor(0xdddddd, 1); // Light gray background document.body.appendChild(threeRenderer.domElement); // Create Three.js scene const scene = new THREE.Scene(); // Set up perspective camera with 70° FOV const threeCamera = new THREE.PerspectiveCamera(70, WIDTH / HEIGHT); threeCamera.position.z = 50; // Move camera back to see the scene scene.add(threeCamera); // Create a simple cube mesh const boxGeometry = new THREE.BoxGeometry(30, 30, 30); const basicMaterial = new THREE.MeshBasicMaterial({ color: 0x0095dd }); // Blue color const cube = new THREE.Mesh(boxGeometry, basicMaterial); scene.add(cube); // === PIXI.JS SETUP === // Create PixiJS renderer that shares the WebGL context with Three.js const pixiRenderer = new WebGLRenderer(); // Initialize PixiJS renderer with shared context await pixiRenderer.init({ context: threeRenderer.getContext(), width: WIDTH, height: HEIGHT, clearBeforeRender: false, // Don't clear the canvas as Three.js will handle that }); // Create PixiJS scene graph const stage = new Container(); // Create a yellow rounded rectangle UI element const uiLayer = new Graphics().roundRect(20, 80, 300, 300, 20).fill(0xffff00); // Add text overlay const text = new Text({ text: 'Pixi and Three.js', style: { fontFamily: 'Arial', fontSize: 24, fill: 'black' }, }); uiLayer.addChild(text); stage.addChild(uiLayer); // Animation loop function loop() { // Rotate cube continuously cube.rotation.x += 0.01; cube.rotation.y += 0.01; // Animate UI layer position using sine wave uiLayer.y = ((Math.sin(Date.now() * 0.001) + 1) * 0.5 * WIDTH) / 2; // Render Three.js scene threeRenderer.resetState(); threeRenderer.render(scene, threeCamera); // Render PixiJS scene pixiRenderer.resetState(); pixiRenderer.render({ container: stage }); // Continue animation loop requestAnimationFrame(loop); } // Start animation loop requestAnimationFrame(loop); // Handle window resizing window.addEventListener('resize', () => { WIDTH = window.innerWidth; HEIGHT = window.innerHeight; // Update Three.js renderer threeRenderer.setSize(WIDTH, HEIGHT); // Update Three.js camera aspect ratio so it renders correctly threeCamera.aspect = WIDTH / HEIGHT; threeCamera.updateProjectionMatrix(); // Update PixiJS renderer pixiRenderer.resize(WIDTH, HEIGHT); }); })(); ``` --- ### Gotchas - **Enable Stencil Buffers:** - When creating the Three.js renderer, ensure `stencil` is set to `true`. This allows PixiJS masks to work correctly. - **Keep Dimensions in Sync:** - Ensure both renderers use the same `width` and `height` to avoid visual mismatches—so be careful when resizing one, you need to resize the other! - **Pass the WebGL Context:** - Pass the WebGL context from Three.js to PixiJS during initialization using `pixiRenderer.init({ context: threeRenderer.getContext() });`. - **Disable Clear Before Render:** - Set `clearBeforeRender: false` when initializing the PixiJS renderer. This prevents PixiJS from clearing the Three.js content that was rendered before it. - Alternatively you can set `clear: false` in the `pixiRenderer.render()` call. eg `pixiRenderer.render({ container: stage, clear: false });`. - **Manage Render Order:** - In this example, Three.js is rendered first, followed by PixiJS for UI layers. However, this order is flexible. You can render pixi -> three -> pixi is you want, just make sure you reset the state when switching renderer. - **Separate Resources:** - Remember that resources like textures are not shared between PixiJS and Three.js. A PixiJS texture cannot be directly used as a Three.js texture and vice versa. --- ### Conclusion Mixing PixiJS and Three.js can be a powerful way to create dynamic and visually appealing applications. By carefully managing the rendering loop and states, you can achieve seamless transitions between 3D and 2D layers. This approach allows you to leverage the strengths of both technologies, creating applications that are both visually stunning and performant. This technique can be used with other renderers too - as long as they have their own way of resetting their state (which the main ones do) you can mix them. Popular 3D engines like Babylon.js and PlayCanvas both support state management through their respective APIs, making them compatible with this mixing approach. This gives you the flexibility to choose the 3D engine that best suits your needs while still leveraging PixiJS's powerful 2D capabilities. --- ## v8 Migration Guide # v8 Migration Guide Welcome to the PixiJS v8 Migration Guide! This document is designed to help you smoothly transition your projects from PixiJS v7 to the latest and greatest PixiJS v8. Please follow these steps to ensure a successful migration. ## Table of Contents 1. [Introduction](#introduction) 2. [Breaking Changes](#breaking-changes) 3. [Deprecated Features](#deprecated-features) 4. [Resources](#resources) ## 1. Introduction {#introduction} PixiJS v8 introduces several exciting changes and improvements that dramatically enhance the performance of the renderer. While we've made efforts to keep the migration process as smooth as possible, some breaking changes are inevitable. This guide will walk you through the necessary steps to migrate your PixiJS v7 project to PixiJS v8. ## 2. Breaking Changes {#breaking-changes} Before diving into the migration process, let's review the breaking changes introduced in PixiJS v8. Make sure to pay close attention to these changes as they may impact your existing codebase. ### Should I Upgrade? Generally, the answer is yes! But currently, there may be reasons that suggest it's best not to upgrade just yet. Ask yourself the following question: - **Does your project leverage existing Pixi libraries that have not yet been migrated to v8?** We are working hard to migrate our key libraries to v8 but did not want this to be a blocker for those who are using pure Pixi. This means some libraries will not have a v8 counterpart just yet. It's best to hold off on migration if this is the case for you. **Migrated** - Filters - Sound - Gif - Storybook - UI - Open Games **Migrating Right Now:** - React - Spine (esoteric version) **To Be Migrated:** - Pixi layers (rather than migrating this, we will likely incorporate it directly into PixiJS v8 as a feature) ### **New Package Structure** Since version 5, PixiJS has utilized individual sub-packages to organize its codebase into smaller units. However, this approach led to issues, such as conflicting installations of different PixiJS versions, causing complications with internal caches. In v8, PixiJS has reverted to a single-package structure. While you can still import specific parts of PixiJS, you only need to install the main package. **Old:** ```ts import { Application } from '@pixi/app'; import { Sprite } from '@pixi/sprite'; ``` **New:** ```ts import { Application, Sprite } from 'pixi.js'; ``` #### Custom Builds PixiJS uses an "extensions" system to add renderer functionality. By default, PixiJS includes many extensions for a comprehensive out-of-the-box experience. However, for full control over features and bundle size, you can manually import specific PixiJS components. ```ts // imported by default import 'pixi.js/accessibility'; import 'pixi.js/app'; import 'pixi.js/events'; import 'pixi.js/filters'; import 'pixi.js/sprite-tiling'; import 'pixi.js/text'; import 'pixi.js/text-bitmap'; import 'pixi.js/text-html'; import 'pixi.js/graphics'; import 'pixi.js/mesh'; import 'pixi.js/sprite-nine-slice'; // not added by default, everyone needs to import these manually import 'pixi.js/advanced-blend-modes'; import 'pixi.js/unsafe-eval'; import 'pixi.js/prepare'; import 'pixi.js/math-extras'; import 'pixi.js/dds'; import 'pixi.js/ktx'; import 'pixi.js/ktx2'; import 'pixi.js/basis'; import { Application } from 'pixi.js'; const app = new Application(); await app.init({ manageImports: false, // disable importing the above extensions }); ``` When initializing the application, you can disable the auto-import feature, preventing PixiJS from importing any extensions automatically. You'll need to import them manually, as demonstrated above. It should also be noted that the `pixi.js/text-bitmap`, also add `Assets` loading functionality. Therefore if you want to load bitmap fonts **BEFORE** initialising the renderer, you will need to import this extension. ```ts import 'pixi.js/text-bitmap'; import { Assets, Application } from 'pixi.js'; await Assets.load('my-font.fnt'); // If 'pixi.js/text-bitmap' is not imported, this will not load await new Application().init(); ``` ### **Async Initialisation** PixiJS will now need to be initialised asynchronously. With the introduction of the WebGPU renderer PixiJS will now need to be awaited before being used **Old:** ```ts import { Application } from 'pixi.js'; const app = new Application(); // do pixi things ``` **New:** ```ts import { Application } from 'pixi.js'; const app = new Application(); (async () => { await app.init({ // application options }); // do pixi things })(); ``` With this change it also means that the `ApplicationOptions` object can now be passed into the `init` function instead of the constructor. ### ** Texture adjustments ** Textures structures have been modified to simplify what was becoming quite a mess behind the scenes in v7. Textures no longer know or manage loading of resources. This needs to be done upfront by you or the assets manager. Textures expect full loaded resources only. This makes things so much easier to manage as the validation of a texture can essentially be done at construction time and left at that! BaseTexture no longer exists. In stead we now have a variety of TextureSources available. A texture source combines the settings of a texture with how to upload and use that texture. In v8 there are the following texture sources: TextureSource - a vanilla texture that you can render too or upload however you wish. (used mainly by render textures) ImageSource - a texture source that contains an image resource of some kind (eg ImageBitmap or html image) CanvasSource - a canvas source that contains a canvas. Used mainly for rendering canvases or rendering to a canvas (webGPU) VideoSource - a texture source that contains a video. Takes care of updating the texture on the GPU to ensure that they stay in sync. BufferSource - a texture source that contains a buffer. What ever you want really! make sure your buffer type and format are compatible! CompressedSource - a texture source that handles compressed textures. Used by the GPU compressed texture formats. Whilst the majority of the time `Assets` will return Textures you may want to make you own! More power to ya! To create a texture source the signature differs from baseTexture. example: ``` const image = new Image(); image.onload = function(){ // create a texture source const source = new ImageSource({ resource: image, }); // create a texture const texture = new Texture({ source }); } image.src = 'myImage.png'; ``` ### **Graphics API Overhaul** There are a few key changes to the Graphics API. In fact this is probably the most changed part of v8. We have added deprecations where possible but below is the rundown of changes: - Instead of beginning a fill or a stroke and then building a shape, v8 asks you to build your shape and then stroke / fill it. The terminology of `Line` has been replaced with the terminology of `Stroke` **Old:** ```ts // red rect const graphics = new Graphics() .beginFill(0xff0000) .drawRect(50, 50, 100, 100) .endFill(); // blue rect with stroke const graphics2 = new Graphics() .lineStyle(2, 'white') .beginFill('blue') .circle(530, 50, 140, 100) .endFill(); ``` **New:** ```ts // red rect const graphics = new Graphics().rect(50, 50, 100, 100).fill(0xff0000); // blue rect with stroke const graphics2 = new Graphics() .rect(50, 50, 100, 100) .fill('blue') .stroke({ width: 2, color: 'white' }); ``` - Shape functions have been renamed. Each drawing function has been simplified into a shorter version of its name. They have the same parameters though: | v7 API Call | v8 API Equivalent | | ------------------ | ----------------- | | drawChamferRect | chamferRect | | drawCircle | circle | | drawEllipse | ellipse | | drawFilletRect | filletRect | | drawPolygon | poly | | drawRect | rect | | drawRegularPolygon | regularPoly | | drawRoundedPolygon | roundPoly | | drawRoundedRect | roundRect | | drawRoundedShape | roundShape | | drawStar | star | - fills functions expect `FillStyle` options or a color, rather than a string of parameters. This also replaces `beginTextureFill` **Old:** ```ts const rect = new Graphics() .beginTextureFill({ texture: Texture.WHITE, alpha: 0.5, color: 0xff0000 }) .drawRect(0, 0, 100, 100) .endFill() .beginFill(0xffff00, 0.5) .drawRect(100, 0, 100, 100) .endFill(); ``` **New:** ```ts const rect = new Graphics() .rect(0, 0, 100, 100) .fill({ texture: Texture.WHITE, alpha: 0.5, color: 0xff0000 }) .rect(100, 0, 100, 100) .fill({ color: 0xffff00, alpha: 0.5 }); ``` - stokes functions expect `StrokeStyle` options or a color, rather than a string of parameters. This also replaces `lineTextureStyle` **Old:** ```ts const rect = new Graphics() .lineTextureStyle({texture:Texture.WHITE, width:10, color:0xFF0000}) .drawRect(0, 0, 100, 100) .endFill() .lineStyle(2, 0xFEEB77); .drawRect(100, 0, 100, 100) .endFill(); ``` **New:** ```ts const rect = new Graphics() .rect(0, 0, 100, 100) .stroke({ texture: Texture.WHITE, width: 10, color: 0xff0000 }) .rect(100, 0, 100, 100) .stroke({ color: 0xfeeb77, width: 2 }); ``` - holes now make use of a new `cut` function. As with `stroke` and `fill`, `cut` acts on the previous shape. **Old:** ```ts const rectAndHole = new Graphics() .beginFill(0x00ff00) .drawRect(0, 0, 100, 100) .beginHole() .drawCircle(50, 50, 20) .endHole() .endFill(); ``` **New:** ```ts const rectAndHole = new Graphics() .rect(0, 0, 100, 100) .fill(0x00ff00) .circle(50, 50, 20) .cut(); ``` - `GraphicsGeometry` has been replaced with `GraphicsContext` this allows for sharing of `Graphics` data more efficiently. **Old:** ```ts const rect = new Graphics() .beginFill(0xff0000) .drawRect(50, 50, 100, 100) .endFill(); const geometry = rect.geometry; const secondRect = new Graphics(geometry); ``` **New:** ```ts const context = new GraphicsContext().rect(50, 50, 100, 100).fill(0xff0000); const rect = new Graphics(context); const secondRect = new Graphics(context); ``` ### Shader changes As we now need to accommodate both WebGL and WebGPU shaders, the way they are constructed has been tweaked to take this into account. The main differences you will notice (this is for shaders in general) is that Textures are no longer considered uniforms (as in they cannot be included in a uniform group). Instead we have the concept of resources. A resource can be a few things including: - TextureSource - A source texture `myTexture.source` - TextureStyle - A texture style `myTexture.style` - UniformGroup - A collection of number based uniforms `myUniforms = new UniformGroup({})` - BufferResource - A buffer that is treated as a uniform group (advanced) creating a webgl only shader now looks like this: **old** ```ts const shader = PIXI.Shader.from(vertex, fragment, uniforms); ``` **new** just WebGL ```ts const shader = Shader.from({ gl: { vertex, fragment }, resources, // resource used from above including uniform groups }); ``` WebGL and WebGPU ```ts const shader = Shader.from({ gl: { vertex, fragment }, gpu: { vertex: { entryPoint: 'mainVert', source, }, fragment: { entryPoint: 'mainFrag', source, }, }, resources, // resource used from above including uniform groups }); ``` Uniforms are also constructed in a slightly different way. When creating them, you now provide the type of variable you want it to be. **old** ```ts const uniformGroup = new UniformGroup({ uTime: 1, }); uniformGroup.uniforms.uTime = 100; ``` **new** ```ts const uniformGroup = new UniformGroup({ uTime:{value:1, type:'f32', }); uniformGroup.uniforms.uTime = 100; // still accessed the same! ``` The best way to play and fully and get to know this new setup please check out the Mesh and Shader examples: **old**: [v7 example](https://pixijs.com/7.x/examples/mesh-and-shaders/triangle-color) **new**: [v8 example](https://pixijs.com/8.x/examples/mesh-and-shaders/triangle-color) ### Filters Filters work almost exactly the same, unless you are constructing a custom one. If this is the case, the shader changes mentioned above need to taken into account. **old** ```ts const filter = new Filter(vertex, fragment, { uTime: 0.0, }); ``` **new** ```ts const filter = new Filter({ glProgram: GlProgram.from({ fragment, vertex, }), resources: { timeUniforms: { uTime: { value: 0.0, type: 'f32' }, }, }, }); ``` **old**: [v7 example](https://pixijs.com/7.x/examples/filters-advanced/custom) **new**: [v8 example](https://pixijs.com/8.x/examples/filters-advanced/custom) If you're using the [community filters](https://github.com/pixijs/filters), note that the `@pixi/filter-*` packages are no-longer maintained for v8, however, you can import directly from the `pixi-filters` package as sub-modules. **\*old** ```ts import { AdjustmentFilter } from '@pixi/filter-adjustment'; ``` **\*new** ```ts import { AdjustmentFilter } from 'pixi-filters/adjustment'; ``` --- ### ParticleContainer `ParticleContainer` has been reworked in v8 to allow for far more particles than before. There are a few key changes you should be aware of: A `ParticleContainer` no longer accepts sprites as its children. Instead, it requires a `Particle` class (or an object that implements the `IParticle` interface), which follows this interface: ``` export interface IParticle { x: number; y: number; scaleX: number; scaleY: number; anchorX: number; anchorY: number; rotation: number; color: number; texture: Texture; } ``` The reason for this change is that sprites come with many extra properties and events that are generally unnecessary when dealing with large numbers of particles. This approach explicitly removes any ambiguity we had in v7, such as "Why doesn't my sprite work with filters?" or "Why can't I nest children in my sprites?" It’s a bit more predictable. Additionally, due to the lightweight nature of particles, this means we can render far more of them! So, no functionality is lost—just an API tweak with a massive performance boost! Particles are also not stored in the `children` array of the `ParticleContainer`, as particles are not technically part of the scene graph (for performance reasons). Instead, they are stored in a flat list called `particleChildren`, which is part of the `ParticleContainer` class. You can modify this array directly for extra speed, or you can use the `addParticle` and `removeParticle` methods to manage your particles. Another optimization is that `ParticleContainer` does not calculate its own bounds, as doing so would negate the performance gains we've created! Instead, it's up to you to provide a `boundsArea` when initializing the `ParticleContainer`. --- **OLD** ```ts const container = new ParticleContainer(); for (let i = 0; i { sprite.onViewUpdate; }); ``` The act of adding and removing the event when a sprite's texture was changed led to an unacceptable performance drop, especially when swapping many textures (imagine shooting games with lots of keyframe textures swapping). This is why we now leave that responsibility to the user. - New Container culling approach With this version of PixiJS we have changed how the `cullable` property works on containers. Previously culling was done for you automatically during the render loop. However, we have moved this logic out and provided users the ability to control when culling happens themselves. With this change we have added a couple of new properties: - `cullable` - Whether or not the container can be culled - `cullArea` - A cull area that will be used instead of the bounds of the container - `cullableChildren` - Whether or not the containers children can be culled. This can help optimise large scenes **New:** ```ts const container = new Container(); const view = new Rectangle(0, 0, 800, 600); container.cullable = true; container.cullArea = new Rectangle(0, 0, 400, 400); container.cullableChildren = false; Culler.shared.cull(myContainer, view); renderer.render(myContainer); ``` There is also a `CullerPlugin` that can be used to automatically call `Culler.shared.cull` every frame if you want to simulate the old behaviour. ```ts import { extensions, CullerPlugin } from 'pixi.js'; extensions.add(CullerPlugin); ``` - Renamed several mesh classes - renamed `SimpleMesh` -> `MeshSimple` - renamed `SimplePlane` -> `MeshPlane` - renamed `SimpleRope` -> `MeshRope` - Deprecations for `Assets` removed **Old:** ```ts import { Assets } from 'pixi.js'; Assets.add('bunny', 'bunny.png'); ``` **New:** ```ts import { Assets } from 'pixi.js'; Assets.add({ alias: 'bunny', src: 'bunny.png' }); ``` - `settings` object has been removed **Old:** ```ts import { settings, BrowserAdapter } from 'pixi.js'; settings.RESOLUTION = 1; settings.FAIL_IF_MAJOR_PERFORMANCE_CAVEAT = false; settings.ADAPTER = BrowserAdapter; ``` **New:** ```ts import { AbstractRenderer, DOMAdapter, BrowserAdapter } from 'pixi.js'; // Can also be passed into the renderer directly e.g `autoDetectRenderer({resolution: 1})` AbstractRenderer.defaultOptions.resolution = 1; // Can also be passed into the renderer directly e.g `autoDetectRenderer({failIfMajorPerformanceCaveat: false})` AbstractRenderer.defaultOptions.failIfMajorPerformanceCaveat = false; // See below for more information about changes to the adapter DOMAdapter.set(BrowserAdapter); ``` - Adapter and Web Worker Changes - `settings.ADAPTER` has been removed and replaced with `DOMAdapter` - `DOMAdapter` is a static class that can be used to set the adapter for the entire application - PixiJS has two adapters built in `BrowserAdapter` and `WebWorkerAdapter` - `BrowserAdapter` is the default adapter and is used when running in the browser - `WebWorkerAdapter` is used when running in a web worker **Old:** ```ts import { settings, WebWorkerAdapter } from 'pixi.js'; settings.ADAPTER = WebWorkerAdapter; settings.ADAPTER.createCanvas(); ``` **New:** ```ts import { DOMAdapter, WebWorkerAdapter } from 'pixi.js'; DOMAdapter.set(WebWorkerAdapter); DOMAdapter.get().createCanvas(); ``` - Application type now accepts Renderer instead of view by @Zyie in https://github.com/pixijs/pixijs/pull/9740 This is to allow `app.renderer` to be typed correctly **Old:** ```ts const app = new Application(); ``` **New:** ```ts // WebGL or WebGPU renderer const app = new Application>(); // WebGL specific renderer const app = new Application>(); // WebGPU specific renderer const app = new Application>(); ``` * `Texture.from` no longer will load a texture from a URL. When using `Texture.from` you will need to pass in a source such as `CanvasSource`/`ImageSource`/`VideoSource` or a resource such as `HTMLImageElement`/`HTMLCanvasElement`/`HTMLVideoElement` or a string that has been loaded through `Assets.load` **Old:** ```ts import { Texture } from 'pixi.js'; const texture = Texture.from('https://i.imgur.com/IaUrttj.png'); ``` **New:** ```ts import { Assets, Texture } from 'pixi.js'; await Assets.load('https://i.imgur.com/IaUrttj.png'); const texture = Texture.from('https://i.imgur.com/IaUrttj.png'); ``` - The `Ticker`'s callback will now pass the `Ticker` instance instead of the delta time. This is to allow for more control over what unit of time is used. **Old:** ```ts Ticker.shared.add((dt) => { bunny.rotation += dt; }); ``` **New:** ```ts Ticker.shared.add((ticker) => { bunny.rotation += ticker.deltaTime; }); ``` - Text parsers have been renamed - `TextFormat` -> `bitmapFontTextParser` - `XMLStringFormat` -> `bitmapFontXMLStringParser` - `XMLFormat` -> `bitmapFontXMLParser` - The default `eventMode` is now `passive` instead of `auto` - `utils` has been removed. All the functions are available as direct imports. **Old:** ```ts import { utils } from 'pixi.js'; utils.isMobile.any(); ``` **New:** ```ts import { isMobile } from 'pixi.js'; isMobile.any(); ``` - `container.getBounds()` now returns a [`Bounds`](https://pixijs.download/release/docs/rendering.Bounds.html) object instead of a [`Rectangle`](https://pixijs.download/release/docs/maths.Rectangle.html) object. You can access the rectangle by using `container.getBounds().rectangle` instead. **Old:** ```ts const bounds = container.getBounds(); ``` **New:** ```ts const bounds = container.getBounds().rectangle; ``` - `container.cacheAsBitmap` has been replaced with `container.cacheAsTexture()`. They do the same things, except we changed the name `cacheAsTexture` as the Bitmap terminology is not really relevant to PixiJS. **Old:** ```ts container.cacheAsBitmap = true; ``` **New:** ```ts container.cacheAsTexture(true); ``` ## 3. Deprecated Features {#deprecated-features} Certain features from PixiJS v7 have been deprecated in v8. While they will still work, it's recommended to update your code to use the new alternatives. Refer to the deprecated features section for details on what to replace them with. - Leaf nodes no longer allow children Only `Containers` can have children. This means that `Sprite`, `Mesh`, `Graphics` etc can no longer have children. To replicate the old behaviour you can create a `Container` and add the leaf nodes to it. **Old:** ```ts const sprite = new Sprite(); const spriteChild = new Sprite(); sprite.addChild(spriteChild); ``` **New:** ```ts const container = new Container(); const sprite = new Sprite(); const spriteChild = new Sprite(); container.addChild(sprite); container.addChild(spriteChild); ``` - `Application.view` replaced with `Application.canvas` **Old:** ```ts const app = new Application({ view: document.createElement('canvas') }); document.body.appendChild(app.view); ``` **New:** ```ts const app = new Application(); await app.init({ view: document.createElement('canvas') }); document.body.appendChild(app.canvas); ``` - `NineSlicePlane` renamed to `NineSliceSprite` - `SCALE_MODES` replaced with `ScaleMode` strings - `SCALE_MODES.NEAREST` -> `'nearest'`, - `SCALE_MODES.LINEAR` -> `'linear'`, - `WRAP_MODES` replaced with `WrapMode` strings - `WRAP_MODES.CLAMP` -> `'clamp-to-edge'`, - `WRAP_MODES.REPEAT` -> `'repeat'`, - `WRAP_MODES.MIRRORED_REPEAT` -> `'mirror-repeat'`, - `DRAW_MODES` replaced with `Topology` strings - `DRAW_MODES.POINTS` -> `'point-list'`, - `DRAW_MODES.LINES` -> `'line-list'`, - `DRAW_MODES.LINE_STRIP` -> `'line-strip'`, - `DRAW_MODES.TRIANGLES` -> `'triangle-list'`, - `DRAW_MODES.TRIANGLE_STRIP` -> `'triangle-strip'`, - Constructors have largely been changed to accept objects instead of multiple arguments **Old:** ```ts const blurFilter = new BlurFilter(8, 4, 1, 5); const displacementFilter = new DisplacementFilter(sprite, 5); const meshGeometry = new MeshGeometry(vertices, uvs, index); const mesh = new Mesh(geometry, shader, state, drawMode); const plane = new PlaneGeometry(width, height, segWidth, segHeight); const nineSlicePlane = new NineSlicePlane( texture, leftWidth, topHeight, rightWidth, bottomHeight, ); const tileSprite = new TileSprite(texture, width, height); const text = new Text('Hello World', style); const bitmapText = new BitmapText('Hello World', style); const htmlText = new HTMLText('Hello World', style); ``` **New:** ```ts const blurFilter = new BlurFilter({ blur: 8, quality: 4, resolution: 1, kernelSize: 5, }); const displacementFilter = new DisplacementFilter({ sprite, scale: 5, }); const meshGeometry = new MeshGeometry({ positions: vertices, uvs, indices: index, topology: 'triangle-list'; shrinkBuffersToFit: boolean; }); const mesh = new Mesh({ geometry shader texture }); const plane = new PlaneGeometry({ width, height, verticesX: segWidth, verticesY: segHeight, }); const nineSliceSprite = new NineSliceSprite({ texture, leftWidth, topHeight, rightWidth, bottomHeight, }); const tileSprite = new TileSprite({ texture, width, height, }); const text = new Text({ text: 'Hello World', style, }); const bitmapText = new BitmapText({ text:'Hello World', style, }); const htmlText = new HTMLText({ text:'Hello World', style, }); ``` - `container.name` is now `container.label` ## 4. Resources {#resources} - [PixiJS v8 Release Notes](https://github.com/pixijs/pixijs/releases?q=v8.0.0&expanded=true) - [PixiJS Discord](https://discord.gg/CPTjeb28nH) - [PixiJS Issues](https://github.com/pixijs/pixijs/issues) --- ## Bug Bounty Program # Bug Bounty Program PixiJS is committed to delivering a reliable, high-performance rendering engine for the web. To support that mission, we’re launching a **Bug Bounty Program** to reward contributors who help make PixiJS more stable and robust. ## How It Works 1. **Bounty Tag Assignment** The PixiJS team will identify eligible issues and apply the **`bounty`** label along with a specific **dollar amount**. Only issues labeled with the `bounty` tag and a dollar amount are eligible for this program. You can find all current bounty-tagged issues [here](https://github.com/pixijs/pixijs/issues?q=is%3Aissue%20state%3Aopen%20label%3A%F0%9F%92%B0Bounty). 2. **Submission Requirements** To claim a bounty, you must: - Submit a **Pull Request (PR)** that fixes the issue. - Link the issue in your PR description. - Include a clear **example or test case** demonstrating that the bug is resolved. - Follow our standard [contribution guidelines](https://github.com/pixijs/pixijs/blob/dev/.github/CONTRIBUTING.md). 3. **Approval Process** - The PixiJS team will review your PR. - If your fix is accepted and merged into the main branch, your bounty is approved. 4. **Requesting Payment** Once your PR is merged: - You can submit a payout request via our [Open Collective](https://opencollective.com/pixijs) page. - Include a link to the merged PR and the bounty issue in your request. - Payments will be processed through Open Collective. ## Terms & Conditions - Only issues **pre-approved** with a `bounty` tag and dollar amount are eligible. - Bounties are awarded **at the PixiJS team's discretion**. We reserve the right to reject fixes that are incomplete, introduce regressions, or do not meet project standards. - You may submit fixes for issues without a bounty tag, but they will not be eligible for financial rewards. - Multiple contributors can submit PRs for the same bounty, but only the PR that gets merged is eligible for payment. - The bounty amount is fixed and non-negotiable. - Abuse, spamming, or low-quality submissions may result in exclusion from the program. ## Sponsoring Bounties If you are a developer or company working on a project and would like to sponsor a one-off bounty, please contact **Matt Karl** [@bigtimebuddy](https://github.com/bigtimebuddy) at **[hello@mattkarl.com](mailto:hello@mattkarl.com)** to arrange the details. Sponsors can make one-time donations directly to our [Open Collective](https://opencollective.com/pixijs) to fund the bounty. We kindly request that sponsors add an additional **10%** to the bounty amount to cover Open Collective's processing fees. ## **Finding Bounty Issues** You can easily find [eligible bounty issues](https://github.com/pixijs/pixijs/issues?q=is%3Aissue%20state%3Aopen%20label%3A%F0%9F%92%B0Bounty) on our GitHub repository. This allows you to focus your contributions on tasks that have a financial reward. ## Why We're Doing This We believe in the power of open source and community collaboration. Our bug bounty program is designed to: - Encourage contributors to tackle important, impactful issues. - Recognize the hard work involved in debugging and fixing complex problems. - Acknowledge that the PixiJS core team is small and often focused on other critical tasks, your contributions help get issues resolved faster. - Make PixiJS even better for everyone. ## Questions? Feel free to ask questions on our [GitHub Discussions](https://github.com/pixijs/pixijs/discussions) or join our [community Discord](https://discord.gg/QrnxmQUPGV). --- ## FAQ # FAQ ## What is PixiJS for? Everything! Pixi.js is a rendering library that will allow you to create rich, interactive graphic experiences, cross-platform applications, and games without having to dive into the WebGL API or grapple with the intricacies of browser and device compatibility. Killer performance with a clean API, means not only will your content be better - but also faster to build! ## Is PixiJS free? PixiJS is and always will be free and Open Source. That said, financial contributions are what make it possible to push PixiJS further, faster. Contributions allow us to commission the PixiJS developer community to accelerate feature development and create more in-depth documentation. Support Us by making a contribution via Open Collective. Go on! It will be a massive help AND make you feel good about yourself, win win ;) ## Where do I get it? Visit our GitHub page to download the very latest version of PixiJS. This is the most up-to-date resource for PixiJS and should always be your first port of call to make sure you are using the latest version. Just click the 'Download' link in the navigation. ## How do I get started? Right here! Take a look through the Resources section for a wealth of information including documentation, forums, tutorials and the Goodboy blog. ## Why should I use PixiJS? Because you care about speed. PixiJS' #1 mantra has always been speed. We really do feel the need! We do everything we can to make PixiJS as streamlined, efficient and fast as possible, whilst balancing it with offering as many crucial and valuable features as we can. ## Is PixiJS a game engine? No. PixiJS is what we've come to think of as a "creation engine". Whilst it is extremely good for making games, the core essence of PixiJS is simply moving things around on screens as quickly and efficiently as possible. It does of course happen that it is absolutely brilliant for making games though! ## Who makes PixiJS? PixiJS is maintained by three core developers who work on the project part-time: - Mat Groves ([@GoodBoyDigital](https://github.com/GoodBoyDigital)) - Sean Burns ([@Zyie](https://github.com/Zyie)) - Matt Karl ([@bigtimebuddy](https://github.com/bigtimebuddy)) The project thrives thanks to our highly active community of contributors and sponsors. As part-time maintainers, your sponsorship directly supports our ability to improve PixiJS, fix bugs, and create better documentation. Consider supporting our work through [GitHub Sponsors](https://github.com/sponsors/pixijs) or [Open Collective](https://opencollective.com/pixijs). ## I found a bug. What should I do? Two things - lets us know via the PixiJS GitHub community and even better yet, if you know how, post a fix! Our Community is stronger in numbers so we're always keen to welcome new contributors into the team to help us shape what PixiJS becomes next. --- ## Culler Plugin # Culler Plugin The `CullerPlugin` automatically skips rendering for offscreen objects in your scene. It does this by using the renderer's screen bounds to determine whether containers (and optionally their children) intersect the view. If they don't, they are **culled**, reducing rendering and update overhead. PixiJS does not enable this plugin by default. You must manually register it using the `extensions` system. ## When Should You Use It? Culling is ideal for: - Large scenes with many offscreen elements - Scrollable or camera-driven environments (e.g. tilemaps, world views) - Optimizing render performance without restructuring your scene graph ## Usage ```ts const app = new Application(); await app.init({ width: 800, height: 600, backgroundColor: 0x222222, }); extensions.add(CullerPlugin); const world = new Container(); world.cullable = true; world.cullableChildren = true; const sprite = new Sprite.from('path/to/image.png'); sprite.cullable = true; // Enable culling for this sprite world.addChild(sprite); app.stage.addChild(world); ``` ### Enabling the Culler Plugin To enable automatic culling in your application: ```ts import { extensions, CullerPlugin } from 'pixi.js'; extensions.add(CullerPlugin); ``` This will override the default `render()` method on your `Application` instance to call `Culler.shared.cull()` before rendering: ```ts // Internally replaces: app.renderer.render({ container: app.stage }); // With: Culler.shared.cull(app.stage, app.renderer.screen); app.renderer.render({ container: app.stage }); ``` ### Configuring Containers for Culling By default, containers are **not culled**. To enable culling for a container, set the following properties: ```ts container.cullable = true; // Enables culling for this container container.cullableChildren = true; // Enables recursive culling for children ``` ### Optional: Define a Custom Cull Area You can define a `cullArea` to override the default bounds check (which uses global bounds): ```ts container.cullArea = new Rectangle(0, 0, 100, 100); ``` This is useful for containers with many children where bounding box calculations are expensive or inaccurate. --- ## Manual Culling with `Culler` If you’re not using the plugin but want to manually cull before rendering: ```ts import { Culler } from 'pixi.js'; const stage = new Container(); // Configure stage and children... Culler.shared.cull(stage, { x: 0, y: 0, width: 800, height: 600 }); renderer.render({ container: stage }); ``` --- ## API Reference - [CullerPlugin](https://pixijs.download/release/docs/app.CullerPlugin.html) --- ## Application # Application The `Application` class provides a modern, extensible entry point to set up rendering in PixiJS. It abstracts common tasks like renderer setup and ticker updates, and is designed to support both WebGL and WebGPU via async initialization. ## Creating an Application Creating an application requires two steps: constructing an instance, then initializing it asynchronously using `.init()`: ```ts import { Application } from 'pixi.js'; const app = new Application(); await app.init({ width: 800, height: 600, backgroundColor: 0x1099bb, }); document.body.appendChild(app.canvas); ``` ### ApplicationOptions Reference The `.init()` method of `Application` accepts a `Partial` object with the following configuration options: | Option | Type | Default | Description | | ------------------------ | ----------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | `autoStart` | `boolean` | `true` | Whether to start rendering immediately after initialization. Setting to `false` will not stop the shared ticker if it's already running. | | `resizeTo` | `Window \| HTMLElement` | — | Element to auto-resize the renderer to match. | | `sharedTicker` | `boolean` | `false` | Use the shared ticker instance if `true`; otherwise, a private ticker is created. | | `preference` | `'webgl' \| 'webgpu'` | `webgl` | Preferred renderer type. | | `useBackBuffer` | `boolean` | `false` | _(WebGL only)_ Use the back buffer when required. | | `forceFallbackAdapter` | `boolean` | `false` | _(WebGPU only)_ Force usage of fallback adapter. | | `powerPreference` | `'high-performance' \| 'low-power'` | `undefined` | Hint for GPU power preference (WebGL & WebGPU). | | `antialias` | `boolean` | — | Enables anti-aliasing. May impact performance. | | `autoDensity` | `boolean` | — | Adjusts canvas size based on `resolution`. Applies only to `HTMLCanvasElement`. | | `background` | `ColorSource` | — | Alias for `backgroundColor`. | | `backgroundAlpha` | `number` | `1` | Alpha transparency for background (0 = transparent, 1 = opaque). | | `backgroundColor` | `ColorSource` | `'black'` | Color used to clear the canvas. Accepts hex, CSS color, or array. | | `canvas` | `ICanvas` | — | A custom canvas instance (optional). | | `clearBeforeRender` | `boolean` | `true` | Whether the renderer should clear the canvas each frame. | | `context` | `WebGL2RenderingContext \| null` | `null` | User-supplied rendering context (WebGL). | | `depth` | `boolean` | — | Enable a depth buffer in the main view. Always `true` for WebGL. | | `height` | `number` | `600` | Initial height of the renderer (in pixels). | | `width` | `number` | `800` | Initial width of the renderer (in pixels). | | `hello` | `boolean` | `false` | Log renderer info and version to the console. | | `multiView` | `boolean` | `false` | Enable multi-canvas rendering. | | `preferWebGLVersion` | `1 \| 2` | `2` | Preferred WebGL version. | | `premultipliedAlpha` | `boolean` | `true` | Assume alpha is premultiplied in color buffers. | | `preserveDrawingBuffer` | `boolean` | `false` | Preserve buffer between frames. Needed for `toDataURL`. | | `resolution` | `number` | 1 | The resolution of the renderer. | | `skipExtensionImports` | `boolean` | `false` | Prevent automatic import of default PixiJS extensions. | | `textureGCActive` | `boolean` | `true` | Enable garbage collection for GPU textures. | | `textureGCCheckCountMax` | `number` | `600` | Frame interval between GC runs (textures). | | `textureGCMaxIdle` | `number` | `3600` | Max idle frames before destroying a texture. | | `textureGCAMaxIdle` | `number` | — | (Appears undocumented; placeholder for internal GC controls.) | ### Customizing Application Options Per Renderer Type You can also override properties based on the renderer type by using the `WebGLOptions` or `WebGPUOptions` interfaces. For example: ```ts import { Application } from 'pixi.js'; const app = new Application(); await app.init({ width: 800, height: 600, backgroundColor: 0x1099bb, webgl: { antialias: true, }, webgpu: { antialias: false, }, }); document.body.appendChild(app.canvas); ``` --- ## Built-In Plugins PixiJS includes: - ✅ **Ticker Plugin** — Updates every frame → [Guide](./ticker-plugin.md) - ✅ **Resize Plugin** — Resizes renderer/canvas → [Guide](./resize-plugin.md) - ➕ **Optional: Culler Plugin** - Culls objects that are out of frame → [Guide](./culler-plugin.md) --- ## Creating a Custom Application Plugin You can create custom plugins for the `Application` class. A plugin must implement the `ApplicationPlugin` interface, which includes `init()` and `destroy()` methods. You can also specify the `extension` type, which is `ExtensionType.Application` for application plugins. Both functions are called with `this` set as the `Application` instance e.g `this.renderer` or `this.stage` is available. The `init()` method is called when the application is initialized and passes the options from the `application.init` call, and the `destroy()` method is called when the application is destroyed. ```ts import type { ApplicationOptions, ApplicationPlugin, ExtensionType } from 'pixi.js'; const myPlugin: ApplicationPlugin = { extension: ExtensionType.Application; init(options: ApplicationOptions) { console.log('Custom plugin init:', this, options); }, destroy() { console.log('Custom plugin destroy'); }, }; ``` Register with: ```ts import { extensions } from 'pixi.js'; extensions.add(myPlugin); ``` ### Adding Types If you are using TypeScript, or are providing a plugin for others to use, you can extend the `ApplicationOptions` interface to include your custom plugins options. ```ts declare global { namespace PixiMixins { interface ApplicationOptions { myPlugin?: import('./myPlugin').PluginOptions | null; } } } await app.init({ myPlugin: { customOption: true, // Now TypeScript will know about this option }, }); ``` --- ## API Reference - [Overview](https://pixijs.download/release/docs/app.html) - [Application](https://pixijs.download/release/docs/app.Application.html) - [ApplicationOptions](https://pixijs.download/release/docs/app.ApplicationOptions.html) - [AutoDetectOptions](https://pixijs.download/release/docs/rendering.AutoDetectOptions.html) - [WebGLOptions](https://pixijs.download/release/docs/rendering.WebGLOptions.html) - [WebGPUOptions](https://pixijs.download/release/docs/rendering.WebGPUOptions.html) - [SharedRendererOptions](https://pixijs.download/release/docs/rendering.SharedRendererOptions.html) - [TickerPlugin](https://pixijs.download/release/docs/app.TickerPlugin.html) - [ResizePlugin](https://pixijs.download/release/docs/app.ResizePlugin.html) - [CullerPlugin](https://pixijs.download/release/docs/app.CullerPlugin.html) --- ## Resize Plugin # Resize Plugin The `ResizePlugin` provides automatic resizing functionality for PixiJS applications. When enabled, it listens to window or element resize events and resizes the application's renderer accordingly. This is useful for: - Making the canvas responsive to the browser window - Maintaining aspect ratio or fitting to containers - Handling layout changes without manual resize calls By default, PixiJS adds this plugin when initializing an `Application`, but you can also register it manually if you're using a custom setup. --- ## Usage ```ts import { Application } from 'pixi.js'; const app = new Application(); await app.init({ width: 800, height: 600, resizeTo: window, }); ``` ### Default Behavior - When using `Application.init()` with no overrides, the `ResizePlugin` is installed automatically: - When `resizeTo` is set, the renderer automatically adjusts to match the dimensions of the target (`window` or `HTMLElement`). - Resizing is throttled using `requestAnimationFrame` to prevent performance issues during rapid resize events. - You can trigger a resize manually with `app.resize()` or cancel a scheduled resize with `app.cancelResize()`. ### Manual Registration If you're managing extensions manually: ```ts import { extensions, ResizePlugin } from 'pixi.js'; extensions.add(ResizePlugin); ``` ### Custom Resize Target You can specify a custom target for resizing. This is useful if you want to resize the canvas to fit a specific element rather than the entire window. ```ts await app.init({ resizeTo: document.getElementById('game-container'), }); ``` --- ## API Reference - [`ResizePlugin`](https://pixijs.download/release/docs/app.ResizePlugin.html) --- ## Ticker Plugin # Ticker Plugin The `TickerPlugin` provides a built-in update loop for your PixiJS `Application`. This loop calls `.render()` at a regular cadence—by default, once per animation frame—and integrates with PixiJS's `Ticker` system for precise control over frame-based updates. PixiJS includes this plugin automatically when you initialize an `Application`, but you can also opt out and add it manually. ## Usage ```ts const app = new Application(); await app.init({ sharedTicker: false, autoStart: true, }); app.ticker.add((ticker) => { // Custom update logic here bunny.rotation += 1 * ticker.deltaTime; }); ``` ### Default Behavior The `TickerPlugin` is included automatically unless disabled: ```ts const app = new Application(); await app.init({ autoStart: true, // Automatically starts the render loop sharedTicker: false, // Use a dedicated ticker }); ``` ### Manual Registration If you're managing extensions yourself: ```ts import { extensions, TickerPlugin } from 'pixi.js'; extensions.add(TickerPlugin); ``` # Shared vs Custom Ticker The plugin supports two modes: | Option | Description | | --------------------- | ------------------------------------------------------------ | | `sharedTicker: true` | Uses `Ticker.shared`, shared across all applications. | | `sharedTicker: false` | Creates a private ticker instance scoped to the application. | ### Behavior Differences - If using a **shared ticker**, other code may also be registering updates, so the order of execution can vary. - If using a **custom ticker**, you get complete control over timing and update order. --- ## Lifecycle Control You can manually stop and start the ticker: ```ts app.stop(); // Stop automatic rendering app.start(); // Resume ``` This is useful for: - Pausing the game or animation - Performance throttling on inactive tabs - Managing visibility events --- ## API Reference - [TickerPlugin](https://pixijs.download/release/docs/app.TickerPlugin.html) - [Ticker](https://pixijs.download/release/docs/ticker.Ticker.html) --- ## Background Loader # Background Loader PixiJS provides a **background loader** that allows you to load assets in the background while your application is running. This is useful for loading large assets or multiple assets without blocking the main thread. This can help improve the responsiveness of your application, reduce the initial loading time, and potentially void showing multiple loading screens to the user. ## Loading Bundles The most effective way to use the background loader is to load bundles of assets. Bundles are groups of assets that are related to each other in some way, such as all the assets for a specific screen or level in your game. By loading bundles in the background, you can ensure that the assets are available when you need them without blocking the main thread. ```ts const manifest = { bundles: [ { name: 'home-screen', assets: [ { alias: 'flowerTop', src: 'https://pixijs.com/assets/flowerTop.png' }, ], }, { name: 'game-screen', assets: [ { alias: 'eggHead', src: 'https://pixijs.com/assets/eggHead.png' }, ], }, ], }; // Initialize the asset system with a manifest await Assets.init({ manifest }); // Start loading both bundles in the background Assets.backgroundLoadBundle(['game-screen']); // Load only the first screen assets immediately const resources = await Assets.loadBundle('home-screen'); ``` ## Loading Individual Assets You can also load individual assets in the background using the `Assets.backgroundLoad()` method. This is useful for loading assets that are not part of a bundle or for loading additional assets after the initial load. ```ts // Load an individual asset in the background Assets.backgroundLoad({ alias: 'flowerTop', src: 'https://pixijs.com/assets/flowerTop.png', }); // Load another asset in the background Assets.backgroundLoad({ alias: 'eggHead', src: 'https://pixijs.com/assets/eggHead.png', }); ``` ## API Reference - [Assets](https://pixijs.download/release/docs/assets.Assets.html) - [BackgroundLoader](https://pixijs.download/release/docs/assets.BackgroundLoader.html) --- ## Compressed Textures # Compressed Textures Compressed textures significantly reduce memory usage and GPU upload time, especially on mobile devices or lower-end hardware. PixiJS supports multiple compressed texture formats, but **you must configure the appropriate loaders** before using them. ## Supported Compressed Texture Formats PixiJS provides built-in support for several widely-used compressed texture formats: | Format | File Extension | Description | | --------- | -------------- | ----------------------------------------------------------------------- | | **DDS** | `.dds` | DirectDraw Surface format, supports DXT variants (S3TC) | | **KTX** | `.ktx` | Khronos format, supports ETC and other schemes | | **KTX2** | `.ktx2` | Modern container supporting Basis Universal and Supercompressed formats | | **Basis** | `.basis` | Highly compressed format that can transcode to multiple GPU formats | ## Registering Loaders PixiJS does **not automatically include compressed texture support**. To use these formats, you must explicitly import the necessary loaders before loading any assets: ```ts import 'pixi.js/dds'; import 'pixi.js/ktx'; import 'pixi.js/ktx2'; import 'pixi.js/basis'; ``` :::info You only need to import the loaders for the formats you're using. These imports must run **before** any call to `Assets.load`. ::: ## Using Compressed Textures in Assets Once loaders are registered, you can load compressed textures just like any other asset: ```ts import 'pixi.js/ktx2'; // Import the KTX2 loader import { Assets } from 'pixi.js'; await Assets.load('textures/background.ktx2'); ``` PixiJS will parse and upload the texture using the correct loader and GPU-compatible transcoding path based on the user's device. --- ## Integration with AssetPack [**AssetPack**](https://pixijs.io/assetpack) supports automatic generation of compressed texture variants during the build step. You can: - Convert `.png` or `.jpg` files into `.basis`, `.ktx2`, or `.dds` formats. - Reference compressed files in your manifest using the same aliases or wildcard patterns. - Use the **same manifest and loading workflow** — PixiJS will resolve and load the best available variant based on the device. ### Example Your manifest might include: ```json { "bundles": [ { "name": "scene", "assets": [ { "alias": "bg", "src": ["assets/bg.ktx2", "assets/bg.basis", "assets/bg.png"] } ] } ] } ``` PixiJS will try to load `bg.ktx2` or `bg.basis` first if the device supports it, falling back to `bg.png` as needed. --- ## Assets # Assets PixiJS has the `Assets` singleton which is used to streamline resource loading. It’s modern, Promise-based, cache-aware, and highly extensible—making it the one stop shop for all PixiJS resource management! ```ts import { Assets } from 'pixi.js'; await Assets.init({ ... }); const texture = await Assets.load('path/to/hero.png'); ``` ## Key Capabilities - **Asynchronous loading** of assets via Promises or async/await. - **Caching** prevents redundant network requests. - **Built-in support** for common media formats (images, video, fonts). - **Custom parsers** and **resolvers** for flexibility. - **Background loading, manifest-based bundles,** and **smart fallbacks**. ## Supported File Types | Type | Extensions | Loaders | | ------------------- | ---------------------------------------------------------------- | --------------------------------- | | Textures | `.png`, `.jpg`, `.gif`, `.webp`, `.avif`, `.svg` | `loadTextures`, `loadSvg` | | Video Textures | `.mp4`, `.m4v`, `.webm`, `.ogg`, `.ogv`, `.h264`, `.avi`, `.mov` | `loadVideoTextures` | | Sprite Sheets | `.json` | `spritesheetAsset` | | Bitmap Fonts | `.fnt`, `.xml`, `.txt` | `loadBitmapFont` | | Web Fonts | `.ttf`, `.otf`, `.woff`, `.woff2` | `loadWebFont` | | JSON | `.json` | `loadJson` | | Text | `.txt` | `loadTxt` | | Compressed Textures | `.basis`, `.dds`, `.ktx`, `.ktx2` | `loadBasis`, `loadDDS`, `loadKTX` | > Need more? Add custom parsers! --- ## Getting started ### Loading Assets Loading an asset with PixiJS is as simple as calling `Assets.load()` and passing in the asset’s URL. This function returns a `Promise` that resolves to the loaded resource—whether that’s a texture, font, JSON, or another supported format. You can provide either an **absolute URL** (e.g. from a CDN): ```ts const texture = await Assets.load('https://example.com/assets/hero.png'); ``` Or a **relative path** within your project: ```ts const texture = await Assets.load('assets/hero.png'); ``` PixiJS will **_typically_** automatically determine how to load the asset based on its **file extension** and will cache the result to avoid redundant downloads. ```typescript import { Application, Assets, Texture } from 'pixi.js'; const app = new Application(); // Application must be initialized before loading assets await app.init({ backgroundColor: 0x1099bb }); // Load a single asset const bunnyTexture = await Assets.load('path/to/bunny.png'); const sprite = new Sprite(bunnyTexture); // Load multiple assets at once const textures = await Assets.load(['path/to/bunny.png', 'path/to/cat.png']); const bunnySprite = new Sprite(textures['path/to/bunny.png']); const catSprite = new Sprite(textures['path/to/cat.png']); ``` ### Repeated Loads Are Safe `Assets` caches by URL or alias. Requests for the same resource return the **same texture**. ```ts const p1 = await Assets.load('bunny.png'); const p2 = await Assets.load('bunny.png'); console.log(p1 === p2); // true ``` ### Asset Aliases You can also use aliases to refer to assets instead of their full URLs. This provides a more convenient way to manage assets, especially when you have long or complex URLs. ```ts await Assets.load({ alias: 'bunny', src: 'path/to/bunny.png' }); const bunnyTexture = Assets.get('bunny'); ``` All Asset APIs support aliases, including `Assets.load()`, `Assets.get()`, and `Assets.unload()`. There is more complex ways of defining assets and you can read about them in the [Resolver](./resolver.md) section. ### Retrieving Loaded Assets You can also retrieve assets that have already been loaded using `Assets.get()`: ```ts await Assets.load('path/to/bunny.png'); const bunnyTexture = Assets.get('path/to/bunny.png'); const sprite = new Sprite(bunnyTexture); ``` This is useful for when you have preloaded your assets elsewhere in your code and want to access them later without having to pass round references from the initial load. ### Unloading Assets To unload an asset, you can use `Assets.unload()`. This will remove the asset from the cache and free up memory. Note that if you try to access the asset after unloading it, you will need to load it again. ```ts await Assets.load('path/to/bunny.png'); const bunnyTexture = Assets.get('path/to/bunny.png'); const sprite = new Sprite(bunnyTexture); // Unload the asset await Assets.unload('path/to/bunny.png'); ``` ### Customizing Asset Loading You can customize the asset loading process by providing options to the `Assets.init()` method. This allows you to set preferences for how assets are loaded, specify a base path for assets, and more. ```ts import { Assets } from 'pixi.js'; await Assets.init({...}); ``` | Option | Type | Description | | --------------------- | ------------------------- | ------------------------------------------------------------- | | `basePath` | `string` | Prefix applied to all relative asset paths (e.g. for CDNs). | | `defaultSearchParams` | `string` | A default URL parameter string to append to all assets loaded | | `skipDetections` | `boolean` | Skip environment detection parsers for assets. | | `manifest` | `Manifest` | A descriptor of named asset bundles and their contents. | | `preferences` | `AssetPreferences` | Specifies preferences for each loader | | `bundleIdentifier` | `BundleIdentifierOptions` | **Advanced** - Override how bundlesIds are generated. | --- ## Advanced Usage There are several advanced features available in the `Assets` API that can help you manage your assets more effectively. You can read more about these features in the rest of the documentation: - [Resolving Assets](./resolver.md) - [Manifests & Bundles (Recommended)](./manifest.md) - [Background Loading](./background-loader.md) - [Compressed Textures](./compressed-textures.md) --- ## API Reference - [Overview](https://pixijs.download/release/docs/assets.html) - [Assets](https://pixijs.download/release/docs/assets.Assets.html) --- ## Manifests & Bundles # Manifests & Bundles PixiJS has a structured and scalable approach to asset management through **Manifests** and **Bundles**. This is the recommended way to manage assets in your PixiJS applications, especially for larger projects or those that require dynamic loading of assets based on context or user interaction. This guide explains what they are, how to use them, and how to generate them efficiently using [AssetPack](https://github.com/pixijs/AssetPack) — a tool designed to automate manifest and bundle creation. --- ## What Is a Manifest? A **Manifest** is a descriptor object that defines your asset loading strategy. It lists all bundles, each of which contains grouped assets by name and alias. This structure allows for lazy-loading assets based on application context (e.g. load screen assets, level-specific content, etc.). ```js const manifest = { bundles: [ { name: 'load-screen', assets: [ { alias: 'background', src: 'sunset.png' }, { alias: 'bar', src: 'load-bar.{png,webp}' }, ], }, { name: 'game-screen', assets: [ { alias: 'character', src: 'robot.png' }, { alias: 'enemy', src: 'bad-guy.png' }, ], }, ], }; ``` ### Initializing With a Manifest To initialize PixiJS asset handling with a manifest: ```js import { Assets } from 'pixi.js'; await Assets.init({ manifest }); ``` Once initialized, you can load bundles by name: ```js const loadScreenAssets = await Assets.loadBundle('load-screen'); const gameScreenAssets = await Assets.loadBundle('game-screen'); ``` It should be noted that you can still load assets directly without loading an entire bundle via their alias: ```js await Assets.init({ manifest }); const background = await Assets.load('background'); const bar = await Assets.load('bar'); ``` --- ## What Is a Bundle? A **Bundle** is a group of assets that are identified by a shared name. While bundles can be pre-defined in a manifest, they can also be dynamically registered at runtime. ### Adding a Bundle Dynamically This approach is helpful for scenarios where you want to define bundles on the fly: ```js import { Assets } from 'pixi.js'; Assets.addBundle('animals', [ { alias: 'bunny', src: 'bunny.png' }, { alias: 'chicken', src: 'chicken.png' }, { alias: 'thumper', src: 'thumper.png' }, ]); const assets = await Assets.loadBundle('animals'); // or load a specific asset from the bundle const bunny = await Assets.load('bunny'); ``` --- ## Recommended Tool: AssetPack Managing manifests and bundles manually can be error-prone. [**AssetPack**](https://pixijs.io/assetpack) is a CLI tool that scans your assets folder and generates optimized manifests and bundles automatically. ### Key Benefits - Organizes assets by directory or pattern - Supports output in PixiJS manifest format - Reduces boilerplate and risk of manual mistakes You can integrate AssetPack into your build pipeline to generate the manifest file and load it using `Assets.init({ manifest })`. --- ## Resolver # Resolver In PixiJS, asset management centers around the concept of `UnresolvedAsset` and `ResolvedAsset`. This system is designed to support multi-format assets, conditional loading, and runtime optimization based on platform capabilities (e.g., WebP support, device resolution, or performance constraints). Rather than specifying a fixed URL, developers describe what assets _could_ be loaded — and PixiJS selects the best option dynamically. ## Resolver Lifecycle The resolution process involves four key steps: 1. **UnresolvedAsset Creation** Assets defined using a string or object are internally normalized into `UnresolvedAsset` instances. These include metadata such as aliases, wildcard paths, parser hints, and custom data. 2. **Source Expansion** The `src` field of an `UnresolvedAsset` can be a string or array of strings. PixiJS expands any wildcard patterns (e.g. `myAsset@{1,2}x.{png,webp}`) into a list of concrete candidate URLs. 3. **Best-Match Selection** PixiJS evaluates all candidate URLs and uses platform-aware heuristics to pick the most suitable source. Factors include supported formats (e.g. WebP vs PNG), device pixel ratio, and custom configuration such as preferred formats. 4. **ResolvedAsset Output** The result is a `ResolvedAsset` containing a specific URL and all required metadata, ready to be passed to the relevant parser and loaded into memory. ## Using Unresolved Assets An `UnresolvedAsset` is the primary structure used to define assets in PixiJS. It allows you to specify the source URL(s), alias(es), and any additional data needed for loading. They are more complex, but are also more powerful. | Field | Type | Description | | ------------------ | -------------------- | ---------------------------------------------------------------------------- | | `alias` | `string \| string[]` | One or more aliases used to reference this asset later. | | `src` | `string \| string[]` | Path or paths to one or more asset candidates. Supports wildcards. | | `loadParser` (opt) | `string` | A specific parser to handle the asset (e.g. `'loadTextures'`, `'loadJson'`). | | `data` (opt) | `any` | Extra data to pass into the loader. This varies by parser type. | ## Examples ### Loading a Single Asset ```ts import { Assets } from 'pixi.js'; await Assets.load({ alias: 'bunny', src: 'images/bunny.png', }); ``` ### Loading with Explicit Parser and Loader Options ```ts await Assets.load({ alias: 'bunny', src: 'images/bunny.png', loadParser: 'loadTextures', data: { alphaMode: 'no-premultiply-alpha', }, }); ``` ### Using Wildcards for Responsive and Format-Aware Loading ```ts await Assets.load({ alias: 'bunny', src: 'images/bunny@{0.5,1,2}x.{png,webp}', }); ``` This pattern expands internally to: ```ts [ 'images/bunny@0.5x.png', 'images/bunny@0.5x.webp', 'images/bunny@1x.png', 'images/bunny@1x.webp', 'images/bunny@2x.png', 'images/bunny@2x.webp', ]; ``` PixiJS will select the best match depending on runtime capabilities (e.g. chooses WebP if supported, 2x if on a high-res display). --- ## Related Tools and Features - **AssetPack**: If you're managing large asset sets, [AssetPack](https://pixijs.io/assetpack) can generate optimized manifests using glob patterns and output `UnresolvedAsset` structures automatically. - **Asset Manifests & Bundles**: Use [manifests and bundles](./manifest.md) to predefine groups of unresolved assets and load them via `Assets.loadBundle`. --- ## SVG's # SVG's ### Overview PixiJS provides powerful support for rendering SVGs, allowing developers to integrate scalable vector graphics seamlessly into their projects. This guide explores different ways to use SVGs in PixiJS, covering real-time rendering, performance optimizations, and potential pitfalls. --- ### Why Use SVGs? SVGs have several advantages over raster images like PNGs: ✅ **Smaller File Sizes** – SVGs can be significantly smaller than PNGs, especially for large but simple shapes. A high-resolution PNG may be several megabytes, while an equivalent SVG could be just a few kilobytes. ✅ **Scalability** – SVGs scale without losing quality, making them perfect for responsive applications and UI elements. ✅ **Editable After Rendering** – Unlike textures, SVGs rendered via Graphics can be modified dynamically (e.g., changing stroke colors, modifying shapes). ✅ **Efficient for Simple Graphics** – If the graphic consists of basic shapes and paths, SVGs can be rendered efficiently as vector graphics. However, SVGs can also be computationally expensive to parse, particularly for intricate illustrations with many paths or effects. --- ### Ways to Render SVGs in PixiJS PixiJS offers two primary ways to render SVGs: 1. **As a Texture** – Converts the SVG into a texture for rendering as a sprite. 2. **As a Graphics Object** – Parses the SVG and renders it as vector geometry. Each method has its advantages and use cases, which we will explore below. --- ## 1. Rendering SVGs as Textures ### Overview SVGs can be loaded as textures and used within Sprites. This method is efficient but does not retain the scalability of vector graphics. ### Example ```ts const svgTexture = await Assets.load('tiger.svg'); const mySprite = new Sprite(svgTexture); ``` ```ts import { Application, Graphics } from 'pixi.js'; (async () => { // Create a new application const app = new Application(); // Initialize the application await app.init({ antialias: true, backgroundColor: 'white', resizeTo: window, }); // Append the application canvas to the document body document.body.appendChild(app.canvas); const graphics = new Graphics().svg(` `); app.stage.addChild(graphics); })(); ``` ```ts import { Application, Assets, Graphics } from 'pixi.js'; (async () => { // Create a new application const app = new Application(); // Initialize the application await app.init({ antialias: true, resizeTo: window }); // Append the application canvas to the document body document.body.appendChild(app.canvas); const tigerSvg = await Assets.load({ src: 'https://pixijs.com/assets/tiger.svg', data: { parseAsGraphicsContext: true, }, }); const graphics = new Graphics(tigerSvg); // line it up as this svg is not centered const bounds = graphics.getLocalBounds(); graphics.pivot.set( (bounds.x + bounds.width) / 2, (bounds.y + bounds.height) / 2, ); graphics.position.set(app.screen.width / 2, app.screen.height / 2); app.stage.addChild(graphics); app.ticker.add(() => { graphics.rotation += 0.01; graphics.scale.set(2 + Math.sin(graphics.rotation)); }); })(); ``` ```ts import { Application, Assets, Sprite } from 'pixi.js'; (async () => { // Create a new application const app = new Application(); // Initialize the application await app.init({ antialias: true, resizeTo: window }); // Append the application canvas to the document body document.body.appendChild(app.canvas); const tigerTexture = await Assets.load({ src: 'https://pixijs.com/assets/tiger.svg', }); const sprite = new Sprite(tigerTexture); // line it up as this svg is not centered const bounds = sprite.getLocalBounds(); sprite.pivot.set( (bounds.x + bounds.width) / 2, (bounds.y + bounds.height) / 2, ); sprite.position.set(app.screen.width / 2, app.screen.height / 2); app.stage.addChild(sprite); app.ticker.add(() => { sprite.rotation += 0.01; sprite.scale.set(2 + Math.sin(sprite.rotation)); }); })(); ``` ```ts import { Application, Assets, Sprite } from 'pixi.js'; (async () => { // Create a new application const app = new Application(); // Initialize the application await app.init({ antialias: true, resizeTo: window }); // Append the application canvas to the document body document.body.appendChild(app.canvas); const tigerTexture = await Assets.load({ src: 'https://pixijs.com/assets/tiger.svg', data: { resolution: 4, }, }); const sprite = new Sprite(tigerTexture); // line it up as this svg is not centered const bounds = sprite.getLocalBounds(); sprite.pivot.set( (bounds.x + bounds.width) / 2, (bounds.y + bounds.height) / 2, ); sprite.position.set(app.screen.width / 2, app.screen.height / 2); app.stage.addChild(sprite); app.ticker.add(() => { sprite.rotation += 0.01; sprite.scale.set(2 + Math.sin(sprite.rotation)); }); })(); ``` ### Scaling Textures You can specify a resolution when loading an SVG as a texture to control its size: This does increase memory usage, but it be of a higher fidelity. ```ts const svgTexture = await Assets.load('path/to.svg', { resolution: 4, // will be 4 times as big! }); const mySprite = new Sprite(svgTexture); ``` This ensures the texture appears at the correct size and resolution. ### Pros & Cons ✅ **Fast to render** (rendered as a quad, not geometry) ✅ **Good for static images** ✅ **Supports resolution scaling for precise sizing** ✅ **Ideal for complex SVGs that do not need crisp vector scaling** (e.g., UI components with fixed dimensions) ❌ **Does not scale cleanly** (scaling may result in pixelation) ❌ **Less flexibility** (cannot modify the shape dynamically) ❌ **Texture Size Limit** A texture can only be up to 4096x4096 pixels, so if you need to render a larger SVG, you will need to use the Graphics method. ### Best Use Cases - Background images - Decorative elements - Performance-critical applications where scaling isn’t needed - Complex SVGs that do not require crisp vector scaling (e.g., fixed-size UI components) --- ## 2. Rendering SVGs as Graphics ### Overview PixiJS can render SVGs as real scalable vector graphics using the `Graphics` class. ### Example ```ts const graphics = new Graphics().svg(''); ``` If you want to use the same SVG multiple times, you can use `GraphicsContext` to share the parsed SVG data across multiple graphics objects, improving performance by parsing it once and reusing it. ```ts const context = new GraphicsContext().svg(''); const graphics1 = new Graphics(context); const graphics2 = new Graphics(context); ``` ### Loading SVGs as Graphics Instead of passing an SVG string directly, you can load an SVG file using PixiJS’s `Assets.load` method. This will return a `GraphicsContext` object, which can be used to create multiple `Graphics` objects efficiently. ```ts const svgContext = await Assets.load('path/to.svg', { parseAsGraphicsContext: true, // If false, it returns a texture instead. }); const myGraphics = new Graphics(svgContext); ``` Since it's loaded via `Assets.load`, it will be cached and reused, much like a texture. ### Pros & Cons ✅ **Retains vector scalability** (no pixelation when zooming) ✅ **Modifiable after rendering** (change colors, strokes, etc.) ✅ **Efficient for simple graphics** ✅ **fast rendering if SVG structure does not change** (no need to reparse) ❌ **More expensive to parse** (complex SVGs can be slow to render) ❌ **Not ideal for static images** ### Best Use Cases - Icons and UI elements that need resizing - A game world that needs to remain crisp as a player zooms in - Interactive graphics where modifying the SVG dynamically is necessary --- ## SVG Rendering Considerations ### Supported Features PixiJS supports most SVG features that can be rendered in a Canvas 2D context. Below is a list of common SVG features and their compatibility: | Feature | Supported | | --------------------------------------- | --------- | | Basic Shapes (rect, circle, path, etc.) | ✅ | | Gradients | ✅ | | Stroke & Fill Styles | ✅ | | Text Elements | ❌ | | Filters (Blur, Drop Shadow, etc.) | ❌ | | Clipping Paths | ✅ | | Patterns | ❌ | | Complex Paths & Curves | ✅ | ### Performance Considerations - **Complex SVGs:** Large or intricate SVGs can slow down rendering start up due to high parsing costs. Use `GraphicsContext` to cache and reuse parsed data. - **Vector vs. Texture:** If performance is a concern, consider using SVGs as textures instead of rendering them as geometry. However, keep in mind that textures take up more memory. - **Real-Time Rendering:** Avoid rendering complex SVGs dynamically. Preload and reuse them wherever possible. --- ## Best Practices & Gotchas ### Best Practices ✅ **Use Graphics for scalable and dynamic SVGs** ✅ **Use Textures for performance-sensitive applications** ✅ **Use `GraphicsContext` to avoid redundant parsing** ✅ **Consider `resolution` when using textures to balance quality and memory** ### Gotchas ⚠ **Large SVGs can be slow to parse** – Optimize SVGs before using them in PixiJS. ⚠ **Texture-based SVGs do not scale cleanly** – Use higher resolution if necessary. ⚠ **Not all SVG features are supported** – Complex filters and text elements may not work as expected. --- By understanding how PixiJS processes SVGs, developers can make informed decisions on when to use `Graphics.svg()`, `GraphicsContext`, or SVG textures, balancing quality and performance for their specific use case. --- ## Renderers # Renderers PixiJS renderers are responsible for drawing your scene to a canvas using either **WebGL/WebGL2** or **WebGPU**. These renderers are high-performance GPU-accelerated engines and are composed of modular systems that manage everything from texture uploads to rendering pipelines. All PixiJS renderers inherit from a common base, which provides consistent methods such as `.render()`, `.resize()`, and `.clear()` as well as shared systems for managing the canvas, texture GC, events, and more. ## Renderer Types | Renderer | Description | Status | | ---------------- | ------------------------------------------------------------------ | --------------- | | `WebGLRenderer` | Default renderer using WebGL/WebGL2. Well supported and stable. | ✅ Recommended | | `WebGPURenderer` | Modern GPU renderer using WebGPU. More performant, still maturing. | 🚧 Experimental | | `CanvasRenderer` | Fallback renderer using 2D canvas. | ❌ Coming-soon | :::info The WebGPU renderer is feature complete, however, inconsistencies in browser implementations may lead to unexpected behavior. It is recommended to use the WebGL renderer for production applications. ::: ## Creating a Renderer You can use `autoDetectRenderer()` to create the best renderer for the environment: ```ts import { autoDetectRenderer } from 'pixi.js'; const renderer = await autoDetectRenderer({ preference: 'webgpu', // or 'webgl' }); ``` Or construct one explicitly: ```ts import { WebGLRenderer, WebGPURenderer } from 'pixi.js'; const renderer = new WebGLRenderer(); await renderer.init(options); ``` ## Rendering a Scene To render a scene, you can use the `render()` method. This will draw the specified container to the screen or a texture: ```ts import { Container } from 'pixi.js'; const container = new Container(); renderer.render(container); // or provide a complete set of options renderer.render({ target: container, clear: true, // clear the screen before rendering transform: new Matrix(), // optional transform to apply to the container }); ``` ## Resizing the Renderer To resize the renderer, use the `resize()` method. This will adjust the canvas size and update the resolution: ```ts renderer.resize(window.innerWidth, window.innerHeight); ``` ## Generating Textures You can generate textures from containers using the `generateTexture()` method. This is useful for creating textures from dynamic content: ```ts import { Sprite } from 'pixi.js'; const sprite = new Sprite(); const texture = renderer.generateTexture(sprite); ``` ## Resetting State To reset the renderer's state, use the `resetState()` method. This is useful when mixing PixiJS with other libraries like Three.js: ```ts function render() { // Render the Three.js scene threeRenderer.resetState(); threeRenderer.render(scene, camera); // Render the PixiJS stage pixiRenderer.resetState(); pixiRenderer.render({ container: stage }); requestAnimationFrame(render); } requestAnimationFrame(render); ``` See our full guide on [mixing PixiJS with Three.js](../../third-party/mixing-three-and-pixi.mdx) for more details. --- ## API Reference - [Overview](https://pixijs.download/release/docs/rendering.html) - [AbstractRenderer](https://pixijs.download/release/docs/rendering.AbstractRenderer.html) - [WebGLRenderer](https://pixijs.download/release/docs/rendering.WebGLRenderer.html) - [WebGPURenderer](https://pixijs.download/release/docs/rendering.WebGPURenderer.html) - [AutoDetectRenderer](https://pixijs.download/release/docs/rendering.html#autoDetectRenderer) --- ## Cache As Texture # Cache As Texture ### Using `cacheAsTexture` in PixiJS The `cacheAsTexture` function in PixiJS is a powerful tool for optimizing rendering in your applications. By rendering a container and its children to a texture, `cacheAsTexture` can significantly improve performance for static or infrequently updated containers. Let's explore how to use it effectively, along with its benefits and considerations. :::info[Note] `cacheAsTexture` is PixiJS v8's equivalent of the previous `cacheAsBitmap` functionality. If you're migrating from v7 or earlier, simply replace `cacheAsBitmap` with `cacheAsTexture` in your code. ::: --- ### What Is `cacheAsTexture`? When you set `container.cacheAsTexture()`, the container is rendered to a texture. Subsequent renders reuse this texture instead of rendering all the individual children of the container. This approach is particularly useful for containers with many static elements, as it reduces the rendering workload. To update the texture after making changes to the container, call: ```javascript container.updateCacheTexture(); ``` and to turn it off, call: ```javascript container.cacheAsTexture(false); ``` --- ### Basic Usage Here's an example that demonstrates how to use `cacheAsTexture`: ```javascript import * as PIXI from 'pixi.js'; (async () => { // Create a new application const app = new Application(); // Initialize the application await app.init({ background: '#1099bb', resizeTo: window }); // Append the application canvas to the document body document.body.appendChild(app.canvas); // load sprite sheet.. await Assets.load('https://pixijs.com/assets/spritesheet/monsters.json'); // holder to store aliens const aliens = []; const alienFrames = [ 'eggHead.png', 'flowerTop.png', 'helmlok.png', 'skully.png', ]; let count = 0; // create an empty container const alienContainer = new Container(); alienContainer.x = 400; alienContainer.y = 300; app.stage.addChild(alienContainer); // add a bunch of aliens with textures from image paths for (let i = 0; i < 100; i++) { const frameName = alienFrames[i % 4]; // create an alien using the frame name.. const alien = Sprite.from(frameName); alien.tint = Math.random() * 0xffffff; alien.x = Math.random() * 800 - 400; alien.y = Math.random() * 600 - 300; alien.anchor.x = 0.5; alien.anchor.y = 0.5; aliens.push(alien); alienContainer.addChild(alien); } // this will cache the container and its children as a single texture // so instead of drawing 100 sprites, it will draw a single texture! alienContainer.cacheAsTexture(); })(); ``` In this example, the `container` and its children are rendered to a single texture, reducing the rendering overhead when the scene is drawn. Play around with the example [here](../../../../examples/basic/cache-as-texture.mdx). ### Advanced Usage Instead of enabling cacheAsTexture with true, you can pass a configuration object which is very similar to texture source options. ```typescript container.cacheAsTexture({ resolution: 2, antialias: true, }); ``` - `resolution` is the resolution of the texture. By default this is the same as you renderer or application. - `antialias` is the antialias mode to use for the texture. Much like the resolution this defaults to the renderer or application antialias mode. --- ### Benefits of `cacheAsTexture` - **Performance Boost**: Rendering a complex container as a single texture avoids the need to process each child element individually during each frame. - **Optimized for Static Content**: Ideal for containers with static or rarely updated children. --- ### Advanced Details - **Memory Tradeoff**: Each cached texture requires GPU memory. Using `cacheAsTexture` trades rendering speed for increased memory usage. - **GPU Limitations**: If your container is too large (e.g., over 4096x4096 pixels), the texture may fail to cache, depending on GPU limitations. --- ### How It Works Internally Under the hood, `cacheAsTexture` converts the container into a render group and renders it to a texture. It uses the same texture cache mechanism as filters: ```javascript container.enableRenderGroup(); container.renderGroup.cacheAsTexture = true; ``` Once the texture is cached, updating it via `updateCacheTexture()` is efficient and incurs minimal overhead. Its as fast as rendering the container normally. --- ### Best Practices #### **DO**: - **Use for Static Content**: Apply `cacheAsTexture` to containers with elements that don't change frequently, such as a UI panel with static decorations. - **Leverage for Performance**: Use `cacheAsTexture` to render complex containers as a single texture, reducing the overhead of processing each child element individually every frame. This is especially useful for containers that contain expensive effects eg filters. - **Switch of Antialiasing**: setting antialiasing to false can give a small performance boost, but the texture may look a bit more pixelated around its children's edges. - **Resolution**: Do adjust the resolution based on your situation, if something is scaled down, you can use a lower resolution.If something is scaled up, you may want to use a higher resolution. But be aware that the higher the resolution the larger the texture and memory footprint. #### **DON'T**: - **Apply to Very Large Containers**: Avoid using `cacheAsTexture` on containers that are too large (e.g., over 4096x4096 pixels), as they may fail to cache due to GPU limitations. Instead, split them into smaller containers. - **Overuse for Dynamic Content**: Flick `cacheAsTexture` on / off frequently on containers, as this results in constant re-caching, negating its benefits. Its better to Cache as texture when you once, and then use `updateCacheTexture` to update it. - **Apply to Sparse Content**: Do not use `cacheAsTexture` for containers with very few elements or sparse content, as the performance improvement will be negligible. - **Ignore Memory Impact**: Be cautious of GPU memory usage. Each cached texture consumes memory, so overusing `cacheAsTexture` can lead to resource constraints. --- ### Gotchas - **Rendering Depends on Scene Visibility**: The cache updates only when the containing scene is rendered. Modifying the layout after setting `cacheAsTexture` but before rendering your scene will be reflected in the cache. - **Containers are rendered with no transform**: Cached items are rendered at their actual size, ignoring transforms like scaling. For instance, an item scaled down by 50%, its texture will be cached at 100% size and then scaled down by the scene. - **Caching and Filters**: Filters may not behave as expected with `cacheAsTexture`. To cache the filter effect, wrap the item in a parent container and apply `cacheAsTexture` to the parent. - **Reusing the texture**: If you want to create a new texture based on the container, its better to use `const texture = renderer.generateTexture(container)` and share that amongst you objects! By understanding and applying `cacheAsTexture` strategically, you can significantly enhance the rendering performance of your PixiJS projects. Happy coding! --- ## Container # Container The `Container` class is the foundation of PixiJS's scene graph system. Containers act as groups of scene objects, allowing you to build complex hierarchies, organize rendering layers, and apply transforms or effects to groups of objects. ## What Is a Container? A `Container` is a general-purpose node that can hold other display objects, **including other containers**. It is used to structure your scene, apply transformations, and manage rendering and interaction. Containers are **not** rendered directly. Instead, they delegate rendering to their children. ```ts import { Container, Sprite } from 'pixi.js'; const group = new Container(); const sprite = Sprite.from('bunny.png'); group.addChild(sprite); ``` ## Managing Children PixiJS provides a robust API for adding, removing, reordering, and swapping children in a container: ```ts const container = new Container(); const child1 = new Container(); const child2 = new Container(); container.addChild(child1, child2); container.removeChild(child1); container.addChildAt(child1, 0); container.swapChildren(child1, child2); ``` You can also remove a child by index or remove all children within a range: ```ts container.removeChildAt(0); container.removeChildren(0, 2); ``` To keep a child’s world transform while moving it to another container, use `reparentChild` or `reparentChildAt`: ```ts otherContainer.reparentChild(child); ``` ### Events Containers emit events when children are added or removed: ```ts group.on('childAdded', (child, parent, index) => { ... }); group.on('childRemoved', (child, parent, index) => { ... }); ``` ### Finding Children Containers support searching children by `label` using helper methods: ```ts const child = new Container({ label: 'enemy' }); container.addChild(child); container.getChildByLabel('enemy'); container.getChildrenByLabel(/^enemy/); // all children whose label starts with "enemy" ``` Set `deep = true` to search recursively through all descendants. ```ts container.getChildByLabel('ui', true); ``` ### Sorting Children Use `zIndex` and `sortableChildren` to control render order within a container: ```ts child1.zIndex = 1; child2.zIndex = 10; container.sortableChildren = true; ``` Call `sortChildren()` to manually re-sort if needed: ```ts container.sortChildren(); ``` :::info Use this feature sparingly, as sorting can be expensive for large numbers of children. ::: ## Optimizing with Render Groups Containers can be promoted to **render groups** by setting `isRenderGroup = true` or calling `enableRenderGroup()`. Use render groups for UI layers, particle systems, or large moving subtrees. See the [Render Groups guide](../../../concepts/render-groups.md) for more details. ```ts const uiLayer = new Container({ isRenderGroup: true }); ``` ## Cache as Texture The `cacheAsTexture` function in PixiJS is a powerful tool for optimizing rendering in your applications. By rendering a container and its children to a texture, `cacheAsTexture` can significantly improve performance for static or infrequently updated containers. When you set `container.cacheAsTexture()`, the container is rendered to a texture. Subsequent renders reuse this texture instead of rendering all the individual children of the container. This approach is particularly useful for containers with many static elements, as it reduces the rendering workload. :::info[Note] `cacheAsTexture` is PixiJS v8's equivalent of the previous `cacheAsBitmap` functionality. If you're migrating from v7 or earlier, simply replace `cacheAsBitmap` with `cacheAsTexture` in your code. ::: ```ts const container = new Container(); const sprite = Sprite.from('bunny.png'); container.addChild(sprite); // enable cache as texture container.cacheAsTexture(); // update the texture if the container changes container.updateCacheTexture(); // disable cache as texture container.cacheAsTexture(false); ``` For more advanced usage, including setting cache options and handling dynamic content, refer to the [Cache as Texture guide](./cache-as-texture.md). --- ## API Reference - [Container](https://pixijs.download/release/docs/scene.Container.html) - [ContainerOptions](https://pixijs.download/release/docs/scene.ContainerOptions.html) - [RenderContainer](https://pixijs.download/release/docs/scene.RenderContainer.html) --- ## Graphics Fill # Graphics Fill If you are new to graphics, please check out the [graphics guide](../graphics) here. This guide dives a bit deeper into a specific aspect of graphics: how to fill them! The `fill()` method in PixiJS is particularly powerful, enabling you to fill shapes with colors, textures, or gradients. Whether you're designing games, UI components, or creative tools, mastering the `fill()` method is essential for creating visually appealing and dynamic graphics. This guide explores the different ways to use the `fill()` method to achieve stunning visual effects. :::info Note The `fillStyles` discussed here can also be applied to Text objects! ::: ## Basic Color Fills When creating a `Graphics` object, you can easily fill it with a color using the `fill()` method. Here's a simple example: ```ts const obj = new Graphics() .rect(0, 0, 200, 100) // Create a rectangle with dimensions 200x100 .fill('red'); // Fill the rectangle with a red color ``` ![alt text](/assets/guides/components/image.png) This creates a red rectangle. PixiJS supports multiple color formats for the `fill()` method. Developers can choose a format based on their needs. For example, CSS color strings are user-friendly and readable, hexadecimal strings are compact and widely used in design tools, and numbers are efficient for programmatic use. Arrays and Color objects offer precise control, making them ideal for advanced graphics. - CSS color strings (e.g., 'red', 'blue') - Hexadecimal strings (e.g., '#ff0000') - Numbers (e.g., `0xff0000`) - Arrays (e.g., `[255, 0, 0]`) - Color objects for precise color control ### Examples: ```ts // Using a number const obj1 = new Graphics().rect(0, 0, 100, 100).fill(0xff0000); // Using a hex string const obj2 = new Graphics().rect(0, 0, 100, 100).fill('#ff0000'); // Using an array const obj3 = new Graphics().rect(0, 0, 100, 100).fill([255, 0, 0]); // Using a Color object const color = new Color(); const obj4 = new Graphics().rect(0, 0, 100, 100).fill(color); ``` ## Fill with a Style Object For more advanced fills, you can use a `FillStyle` object. This allows for additional customization, such as setting opacity: ```ts const obj = new Graphics().rect(0, 0, 100, 100).fill({ color: 'red', alpha: 0.5, // 50% opacity }); ``` ![alt text](/assets/guides/components/image-1.png) ## Fill with Textures Filling shapes with textures is just as simple: ```ts const texture = await Assets.load('assets/image.png'); const obj = new Graphics().rect(0, 0, 100, 100).fill(texture); ``` ![alt text](/assets/guides/components/image-2.png) ### Local vs. Global Texture Space Textures can be applied in two coordinate spaces: - **Local Space** (Default): The texture coordinates are mapped relative to the shape's dimensions and position. The texture coordinates use a normalized coordinate system where (0,0) is the top-left and (1,1) is the bottom-right of the shape, regardless of its actual pixel dimensions. For example, if you have a 300x200 pixel texture filling a 100x100 shape, the texture will be scaled to fit exactly within those 100x100 pixels. The texture's top-left corner (0,0) will align with the shape's top-left corner, and the texture's bottom-right corner (1,1) will align with the shape's bottom-right corner, stretching or compressing the texture as needed. ```ts const shapes = new PIXI.Graphics() .rect(50, 50, 100, 100) .circle(250, 100, 50) .star(400, 100, 6, 60, 40) .roundRect(500, 50, 100, 100, 10) .fill({ texture, textureSpace: 'local', // default! }); ``` ![alt text](/assets/guides/components/image-13.png) - **Global Space**: Set `textureSpace: 'global'` to make the texture position and scale relative to the Graphics object's coordinate system. Despite the name, this isn't truly "global" - the texture remains fixed relative to the Graphics object itself, maintaining its position even when the object moves or scales. See how the image goes across all the shapes (in the same graphics) below: ```ts const shapes = new PIXI.Graphics() .rect(50, 50, 100, 100) .circle(250, 100, 50) .star(400, 100, 6, 60, 40) .roundRect(500, 50, 100, 100, 10) .fill({ texture, textureSpace: 'global', }); ``` ![alt text](/assets/guides/components/image-11.png) ### Using Matrices with Textures To modify texture coordinates, you can apply a transformation matrix, which is a mathematical tool used to scale, rotate, or translate the texture. If you're unfamiliar with transformation matrices, they allow for precise control over how textures are rendered, and you can explore more about them [here](https://learnwebgl.brown37.net/10_surface_properties/texture_mapping_transforms.html#:~:text=Overview%C2%B6,by%2D4%20transformation%20matrix). ```ts const matrix = new Matrix().scale(0.5, 0.5); const obj = new Graphics().rect(0, 0, 100, 100).fill({ texture: texture, matrix: matrix, // scale the texture down by 2 }); ``` ![alt text](/assets/guides/components/image-4.png) ### Texture Gotcha's 1. **Sprite Sheets**: If using a texture from a sprite sheet, the entire source texture will be used. To use a specific frame, create a new texture: ```ts const spriteSheetTexture = Texture.from('assets/my-sprite-sheet.png'); const newTexture = renderer.generateTexture(Sprite.from(spriteSheetTexture)); const obj = new Graphics().rect(0, 0, 100, 100).fill(newTexture); ``` 2. **Power of Two Textures**: Textures should be power-of-two dimensions for proper tiling in WebGL1 (WebGL2 and WebGPU are fine). ## Fill with Gradients PixiJS supports both linear and radial gradients, which can be created using the `FillGradient` class. Gradients are particularly useful for adding visual depth and dynamic styling to shapes and text. ### Linear Gradients Linear gradients create a smooth color transition along a straight line. Here is an example of a simple linear gradient: ```ts const gradient = new FillGradient({ type: 'linear', colorStops: [ { offset: 0, color: 'yellow' }, { offset: 1, color: 'green' }, ], }); const obj = new Graphics().rect(0, 0, 100, 100).fill(gradient); ``` ![alt text](/assets/guides/components/image-5.png) You can control the gradient direction with the following properties: - `start {x, y}`: These define the starting point of the gradient. For example, in a linear gradient, this is where the first color stop is positioned. These values are typically expressed in relative coordinates (0 to 1), where `0` represents the left/top edge and `1` represents the right/bottom edge of the shape. - `end {x, y}`: These define the ending point of the gradient. Similar to `start {x, y}`, these values specify where the last color stop is positioned in the shape's local coordinate system. Using these properties, you can create various gradient effects, such as horizontal, vertical, or diagonal transitions. For example, setting `start` to `{x: 0, y: 0}` and `end` to `{x: 1, y: 1}` would result in a diagonal gradient from the top-left to the bottom-right of the shape. ```ts const diagonalGradient = new FillGradient({ type: 'linear', start: { x: 0, y: 0 }, end: { x: 1, y: 1 }, colorStops: [ { offset: 0, color: 'yellow' }, { offset: 1, color: 'green' }, ], }); ``` ![alt text](/assets/guides/components/image-6.png) ### Radial Gradients Radial gradients create a smooth color transition in a circular pattern. Unlike linear gradients, they blend colors from one circle to another. Here is an example of a simple radial gradient: ```ts const gradient = new FillGradient({ type: 'radial', colorStops: [ { offset: 0, color: 'yellow' }, { offset: 1, color: 'green' }, ], }); const obj = new Graphics().rect(0, 0, 100, 100).fill(gradient); ``` ![alt text](/assets/guides/components/image-7.png) You can control the gradient's shape and size using the following properties: - `center {x, y}`: These define the center of the inner circle where the gradient starts. Typically, these values are expressed in relative coordinates (0 to 1), where `0.5` represents the center of the shape. - `innerRadius`: The radius of the inner circle. This determines the size of the gradient's starting point. - `outerCenter {x, y}`: These define the center of the outer circle where the gradient ends. Like `center {x, y}`, these values are also relative coordinates. - `outerRadius`: The radius of the outer circle. This determines the size of the gradient's ending point. By adjusting these properties, you can create a variety of effects, such as small, concentrated gradients or large, expansive ones. For example, setting a small `r0` and a larger `r1` will create a gradient that starts does not start to transition until the inner circle radius is reached. ```ts const radialGradient = new FillGradient({ type: 'radial', center: { x: 0.5, y: 0.5 }, innerRadius: 0.25, outerCenter: { x: 0.5, y: 0.5 }, outerRadius: 0.5, colorStops: [ { offset: 0, color: 'blue' }, { offset: 1, color: 'red' }, ], }); const obj = new Graphics().rect(0, 0, 100, 100).fill(gradient); ``` ![alt text](/assets/guides/components/image-8.png) ### Gradient Gotcha's 1. **Memory Management**: Use `fillGradient.destroy()` to free up resources when gradients are no longer needed. 2. **Animation**: Update existing gradients instead of creating new ones for better performance. 3. **Custom Shaders**: For complex animations, custom shaders may be more efficient. 4. **Texture and Matrix Limitations**: Under the hood, gradient fills set both the texture and matrix properties internally. This means you cannot use a texture fill or matrix transformation at the same time as a gradient fill. ### Combining Textures and Colors You can combine a texture or gradients with a color tint and alpha to achieve more complex and visually appealing effects. This allows you to overlay a color on top of the texture or gradient, adjusting its transparency with the alpha value. ```ts const gradient = new FillGradient({ colorStops: [ { offset: 0, color: 'blue' }, { offset: 1, color: 'red' }, ], }); const obj = new Graphics().rect(0, 0, 100, 100).fill({ fill: gradient, color: 'yellow', alpha: 0.5, }); ``` ![alt text](/assets/guides/components/image-10.png) ```ts const obj = new Graphics().rect(0, 0, 100, 100).fill({ texture: texture, color: 'yellow', alpha: 0.5, }); ``` ![alt text](/assets/guides/components/image-9.png) --- Hopefully, this guide has shown you how easy and powerful fills can be when working with graphics (and text!). By mastering the `fill()` method, you can unlock endless possibilities for creating visually dynamic and engaging graphics in PixiJS. Have fun! --- ## Graphics Pixel Line import { Sandpack } from '@codesandbox/sandpack-react'; import { dracula } from '@codesandbox/sandpack-themes'; # Graphics Pixel Line The `pixelLine` property is a neat feature of the PixiJS Graphics API that allows you to create lines that remain 1 pixel thick, regardless of scaling or zoom level. As part of the Graphics API, it gives developers all the power PixiJS provides for building and stroking shapes. This feature is especially useful for achieving crisp, pixel-perfect visuals, particularly in retro-style or grid-based games, technical drawing, or UI rendering. In this guide, we'll dive into how this property works, its use cases, and the caveats you should be aware of when using it. --- ```ts import { Application, Container, Graphics, Text } from 'pixi.js'; /** * Creates a grid pattern using Graphics lines * @param graphics - The Graphics object to draw on * @returns The Graphics object with the grid drawn */ function buildGrid(graphics) { // Draw 10 vertical lines spaced 10 pixels apart for (let i = 0; i < 11; i++) { // Move to top of each line (x = i*10, y = 0) graphics .moveTo(i * 10, 0) // Draw down to bottom (x = i*10, y = 100) .lineTo(i * 10, 100); } // Draw 10 horizontal lines spaced 10 pixels apart for (let i = 0; i < 11; i++) { // Move to start of each line (x = 0, y = i*10) graphics .moveTo(0, i * 10) // Draw across to end (x = 100, y = i*10) .lineTo(100, i * 10); } return graphics; } (async () => { // Create and initialize a new PixiJS application const app = new Application(); await app.init({ antialias: true, resizeTo: window }); document.body.appendChild(app.canvas); // Create two grids - one with pixel-perfect lines and one without const gridPixel = buildGrid(new Graphics()).stroke({ color: 0xffffff, pixelLine: true, width: 1, }); const grid = buildGrid(new Graphics()).stroke({ color: 0xffffff, pixelLine: false, }); // Position the grids side by side grid.x = -100; grid.y = -50; gridPixel.y = -50; // Create a container to hold both grids const container = new Container(); container.addChild(grid, gridPixel); // Center the container on screen container.x = app.screen.width / 2; container.y = app.screen.height / 2; app.stage.addChild(container); // Animation variables let count = 0; // Add animation to scale the grids over time app.ticker.add(() => { count += 0.01; container.scale = 1 + (Math.sin(count) + 1) * 2; }); // Add descriptive label const label = new Text({ text: 'Grid Comparison: Standard Lines (Left) vs Pixel-Perfect Lines (Right)', style: { fill: 0xffffff }, }); // Position label in top-left corner label.position.set(20, 20); label.width = app.screen.width - 40; label.scale.y = label.scale.x; app.stage.addChild(label); })(); ``` ## How to use `pixelLine`? Here’s a simple example: ```ts // Create a Graphics object and draw a pixel-perfect line let graphics = new Graphics() .moveTo(0, 0) .lineTo(100, 100) .stroke({ color: 0xff0000, pixelLine: true }); // Add it to the stage app.stage.addChild(graphics); // Even if we scale the Graphics object, the line remains 1 pixel wide graphics.scale.set(2); ``` In this example, no matter how you transform or zoom the `Graphics` object, the red line will always appear 1 pixel thick on the screen. --- ## Why Use `pixelLine`? Pixel-perfect lines can be incredibly useful in a variety of scenarios. Here are some common use cases: ### 1. **Retro or Pixel Art Games** - Pixel art games rely heavily on maintaining sharp, precise visuals. The `pixelLine` property ensures that lines do not blur or scale inconsistently with other pixel elements. - Example: Drawing pixel-perfect grids for tile-based maps. ```ts // Create a grid of vertical and horizontal lines const grid = new Graphics(); // Draw 10 vertical lines spaced 10 pixels apart // Draw vertical lines for (let i = 0; i < 10; i++) { // Move to top of each line (x = i*10, y = 0) grid .moveTo(i * 10, 0) // Draw down to bottom (x = i*10, y = 100) .lineTo(i * 10, 100); } // Draw horizontal lines for (let i = 0; i < 10; i++) { // Move to start of each line (x = 0, y = i*10) grid .moveTo(0, i * 10) // Draw across to end (x = 100, y = i*10) .lineTo(100, i * 10); } // Stroke all lines in white with pixel-perfect width grid.stroke({ color: 0xffffff, pixelLine: true }); ``` --- ### 2. **UI and HUD Elements** - For UI elements such as borders, separators, or underlines, a consistent 1-pixel thickness provides a professional, clean look. - Example: Drawing a separator line in a menu or a progress bar border. ```ts // Create a separator line that will always be 1 pixel thick const separator = new Graphics() // Start at x=0, y=50 .moveTo(0, 50) // Draw a horizontal line 200 pixels to the right .lineTo(200, 50) // Stroke in green with pixel-perfect 1px width .stroke({ color: 0x00ff00, pixelLine: true }); ``` --- ### 3. **Debugging and Prototyping** - Use pixel-perfect lines to debug layouts, collision boxes, or grids. Since the lines don’t scale, they offer a consistent reference point during development. - Example: Displaying collision boundaries in a physics-based game. ```ts // Create a debug box with pixel-perfect stroke const graphicsBox = new Graphics() .rect(0, 0, 100, 100) .stroke({ color: 0xff00ff, pixelLine: true }); /** * Updates the debug box to match the bounds of a given object * @param {Container} obj - The object to draw bounds for */ function drawDebugBounds(obj) { // Get the bounds of the object let bounds = obj.getBounds().rectangle; // Position and scale the debug box to match the bounds // this is faster than using `moveTo` and `lineTo` each frame! graphicsBox.position.set(bounds.x, bounds.y); graphicsBox.scale.set(bounds.width / 100, bounds.height / 100); } ``` --- ## How it works This is achieved under the hood using WebGL or WebGPU's native line rendering methods when `pixelLine` is set to `true`. Fun fact its actually faster to draw a pixel line than a regular line. This is because of two main factors: 1. **Simpler Drawing Process**: Regular lines in PixiJS (when `pixelLine` is `false`) need extra steps to be drawn. PixiJS has to figure out the thickness of the line and create a shape that looks like a line but is actually made up of triangles. 2. **Direct Line Drawing**: When using `pixelLine`, we can tell the graphics card "just draw a line from point A to point B" and it knows exactly what to do. This is much simpler and faster than creating and filling shapes. Think of it like drawing a line on paper - `pixelLine` is like using a pen to draw a straight line, while regular lines are like having to carefully color in a thin rectangle. The pen method is naturally faster and simpler! ## Caveats and Gotchas While the `pixelLine` property is incredibly useful, there are some limitations and things to keep in mind: ### 1. **Its 1px thick, thats it!** - The line is always 1px thick, there is no way to change this as its using the GPU to draw the line. ### 2. **Hardware may render differently** - Different GPUs and graphics hardware may render the line slightly differently due to variations in how they handle line rasterization. For example, some GPUs may position the line slightly differently or apply different anti-aliasing techniques. This is an inherent limitation of GPU line rendering and is beyond PixiJS's control. ### 4. **Scaling Behavior** - While the line thickness remains constant, other properties (e.g., position or start/end points) are still affected by scaling. This can sometimes create unexpected results if combined with other scaled objects. This is a feature not a bug :) ### Example: Box with Pixel-Perfect Stroke Here's an example of a filled box with a pixel-perfect stroke. The box itself scales and grows, but the stroke remains 1 pixel wide: ```ts // Create a Graphics object and draw a filled box with a pixel-perfect stroke let box = new Graphics() .rect(0, 0, 100, 100) .fill('white') .stroke({ color: 0xff0000, pixelLine: true }); // Add it to the stage app.stage.addChild(box); // Scale the box box.scale.set(2); ``` In this example, the blue box grows as it scales, but the red stroke remains at 1 pixel thickness, providing a crisp outline regardless of the scaling. --- ## When to Avoid Using `pixelLine` - **You want a line that is not 1px thick:** Don't use `pixelLine`. - **You want the line to scale:** Don't use `pixelLine` --- ## Conclusion The `pixelLine` property is a super useful to have in the PixiJS toolbox for developers looking to create sharp, pixel-perfect lines that remain consistent under transformation. By understanding its strengths and limitations, you can incorporate it into your projects for clean, professional results in both visual and functional elements. --- ## Graphics # Graphics [Graphics](https://pixijs.download/release/docs/scene.Graphics.html) is a powerful and flexible tool for rendering shapes such as rectangles, circles, stars, and custom polygons. It can also be used to create complex shapes by combining multiple primitives, and it supports advanced features like gradients, textures, and masks. ```ts import { Graphics } from 'pixi.js'; const graphics = new Graphics().rect(50, 50, 100, 100).fill(0xff0000); ``` ## **Available Shapes** PixiJS v8 supports a variety of shape primitives: ### Basic Primitives - Line - Rectangle - Rounded Rectangle - Circle - Ellipse - Arc - Bezier / Quadratic Curves ### Advanced Primitives - Chamfer Rect - Fillet Rect - Regular Polygon - Star - Rounded Polygon - Rounded Shape ```ts const graphics = new Graphics() .rect(50, 50, 100, 100) .fill(0xff0000) .circle(200, 200, 50) .stroke(0x00ff00) .lineStyle(5) .moveTo(300, 300) .lineTo(400, 400); ``` ### SVG Support You can also load SVG path data, although complex hole geometries may render inaccurately due to Pixi's performance-optimized triangulation system. ```ts let shape = new Graphics().svg(` `); ``` ## **GraphicsContext** The `GraphicsContext` class is the core of PixiJS's new graphics model. It holds all the drawing commands and styles, allowing the same shape data to be reused by multiple `Graphics` instances: ```ts const context = new GraphicsContext().circle(100, 100, 50).fill('red'); const shapeA = new Graphics(context); const shapeB = new Graphics(context); // Shares the same geometry ``` This pattern is particularly effective when rendering repeated or animated shapes, such as frame-based SVG swaps: ```ts let frames = [ new GraphicsContext().circle(100, 100, 50).fill('red'), new GraphicsContext().rect(0, 0, 100, 100).fill('red'), ]; let graphic = new Graphics(frames[0]); function update() { graphic.context = frames[1]; // Very cheap operation } ``` :::info If you don't explicitly pass a `GraphicsContext` when creating a `Graphics` object, then internally, it will have its own context, accessible via `myGraphics.context`. ::: ### Destroying a GraphicsContext When you destroy a `GraphicsContext`, all `Graphics` instances that share it will also be destroyed. This is a crucial point to remember, as it can lead to unexpected behavior if you're not careful. ```ts const context = new GraphicsContext().circle(100, 100, 50).fill('red'); const shapeA = new Graphics(context); const shapeB = new Graphics(context); // Shares the same geometry shapeA.destroy({ context: true }); // Destroys both shapeA and shapeB ``` ## **Creating Holes** Use `.cut()` to remove a shape from the previous one: ```ts const g = new Graphics() .rect(0, 0, 100, 100) .fill(0x00ff00) .circle(50, 50, 20) .cut(); // Creates a hole in the green rectangle ``` Ensure the hole is fully enclosed within the shape to avoid triangulation errors. ## **Graphics Is About Building, Not Drawing** Despite the terminology of functions like `.rect()` or `.circle()`, `Graphics` does not immediately draw anything. Instead, each method builds up a list of geometry primitives stored inside a `GraphicsContext`. These are then rendered when the object is drawn to the screen or used in another context, such as a mask. ```ts const graphic = new Graphics().rect(0, 0, 200, 100).fill(0xff0000); app.stage.addChild(graphic); // The rendering happens here ``` You can think of `Graphics` as a blueprint builder: it defines what to draw, but not when to draw it. This is why `Graphics` objects can be reused, cloned, masked, and transformed without incurring extra computation until they're actually rendered. ## **Performance Best Practices** - **Do not clear and rebuild graphics every frame**. If your content is dynamic, prefer swapping prebuilt `GraphicsContext` objects instead of recreating them. - **Use `Graphics.destroy()`** to clean up when done. Shared contexts are not auto-destroyed. - **Use many simple `Graphics` objects** over one complex one to maintain GPU batching. - **Avoid transparent overlap** unless you understand blend modes; overlapping semi-transparent primitives will interact per primitive, not post-composition. ## **Caveats and Gotchas** - **Memory Leaks**: Call `.destroy()` when no longer needed. - **SVG and Holes**: Not all SVG hole paths triangulate correctly. - **Changing Geometry**: Use `.clear()` sparingly. Prefer swapping contexts. - **Transparency and Blend Modes**: These apply per primitive. Use `RenderTexture` if you want to flatten effects. --- ## **API Reference** - [Graphics](https://pixijs.download/release/docs/scene.Graphics.html) - [GraphicsContext](https://pixijs.download/release/docs/scene.GraphicsContext.html) - [FillStyle](https://pixijs.download/release/docs/scene.FillStyle.html) - [StrokeStyle](https://pixijs.download/release/docs/scene.StrokeStyle.html) --- ## Scene Objects # Scene Objects In PixiJS, scene objects are the building blocks of your application’s display hierarchy. They include **containers**, **sprites**, **text**, **graphics**, and other drawable entities that make up the **scene graph**—the tree-like structure that determines what gets rendered, how, and in what order. ## Containers vs. Leaf Nodes Scene objects in PixiJS can be divided into **containers** and **leaf nodes**: ### Containers `Container` is the **base class** for all scene objects in v8 (replacing the old `DisplayObject`). - Can have children. - Commonly used to group objects and apply transformations (position, scale, rotation) to the group. - Examples: `Application.stage`, user-defined groups. ```ts const group = new Container(); group.addChild(spriteA); group.addChild(spriteB); ``` ### Leaf Nodes Leaf nodes are renderable objects that should not have children. In v8, **only containers should have children**. Examples of leaf nodes include: - `Sprite` - `Text` - `Graphics` - `Mesh` - `TilingSprite` - `HTMLText` Attempting to add children to a leaf node will not result in a runtime error, however, you may run into unexpected rendering behavior. Therefore, If nesting is required, wrap leaf nodes in a `Container`. **Before v8 (invalid in v8):** ```ts const sprite = new Sprite(); sprite.addChild(anotherSprite); // ❌ Invalid in v8 ``` **v8 approach:** ```ts const group = new Container(); group.addChild(sprite); group.addChild(anotherSprite); // ✅ Valid ``` ## Transforms All scene objects in PixiJS have several properties that control their position, rotation, scale, and alpha. These properties are inherited by child objects, allowing you to apply transformations to groups of objects easily. You will often use these properties to position and animate objects in your scene. | Property | Description | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | **position** | X- and Y-position are given in pixels and change the position of the object relative to its parent, also available directly as `object.x` / `object.y` | | **rotation** | Rotation is specified in radians, and turns an object clockwise (0.0 - 2 \* Math.PI) | | **angle** | Angle is an alias for rotation that is specified in degrees instead of radians (0.0 - 360.0) | | **pivot** | Point the object rotates around, in pixels - also sets origin for child objects | | **alpha** | Opacity from 0.0 (fully transparent) to 1.0 (fully opaque), inherited by children | | **scale** | Scale is specified as a percent with 1.0 being 100% or actual-size, and can be set independently for the x and y axis | | **skew** | Skew transforms the object in x and y similar to the CSS skew() function, and is specified in radians | | **anchor?** | Anchor is a percentage-based offset for the sprite's position and rotation. This is different from the `pivot` property, which is a pixel-based offset. | ### **Anchor vs Pivot** Some leaf nodes have an additional property called `anchor`, which is a percentage-based offset for the nodes position and rotation. This is different from the `pivot` property, which is a pixel-based offset. Understanding the difference between anchor and pivot is critical when positioning or rotating a node. :::info Setting either pivot or anchor visually moves the node. This differs from CSS where changing `transform-origin` does not affect the element's position. ::: #### **Anchor** - Available only on `Sprite` - Defined in normalized values `(0.0 to 1.0)` - `(0, 0)` is the top-left, `(0.5, 0.5)` is the center - Changes both position and rotation origin ```ts sprite.anchor.set(0.5); // center sprite.rotation = Math.PI / 4; // Rotate 45 degrees around the center ``` #### **Pivot** - Available on all `Container`s - Defined in **pixels**, not normalized ```ts const sprite = new Sprite(texture); sprite.width = 100; sprite.height = 100; sprite.pivot.set(50, 50); // Center of the container container.rotation = Math.PI / 4; // Rotate 45 degrees around the pivot ``` ## Measuring Bounds There are two types of bounds in PixiJS: - **Local bounds** represent the object’s dimensions in its own coordinate space. Use `getLocalBounds()`. - **Global bounds** represent the object's bounding box in world coordinates. Use `getBounds()`. ```ts const local = container.getLocalBounds(); const global = container.getBounds(); ``` If performance is critical you can also provide a custom `boundsArea` to avoid per-child measurement entirely. ### Changing size To change the size of a container, you can use the `width` and `height` properties. This will scale the container to fit the specified dimensions: ```ts const container = new Container(); container.width = 100; container.height = 200; ``` Setting the `width` and `height` individually can be an expensive operation, as it requires recalculating the bounds of the container and its children. To avoid this, you can use `setSize()` to set both properties at once: ```ts const container = new Container(); container.setSize(100, 200); const size = container.getSize(); // { width: 100, height: 200 } ``` This method is more efficient than setting `width` and `height` separately, as it only requires one bounds calculation. ## Masking Scene Objects PixiJS supports **masking**, allowing you to restrict the visible area of a scene object based on another object's shape. This is useful for creating effects like cropping, revealing, or hiding parts of your scene. ### Types of Masks - **Graphics-based masks**: Use a `Graphics` object to define the shape. - **Sprite-based masks**: Use a `Sprite` or other renderable object. ```ts const shape = new Graphics().circle(100, 100, 50).fill(0x000000); const maskedSprite = new Sprite(texture); maskedSprite.mask = shape; stage.addChild(shape); stage.addChild(maskedSprite); ``` ### Inverse Masks To create an inverse mask, you can use the `setMask` property and set its `inverse` option to `true`. This will render everything outside the mask. ```ts const inverseMask = new Graphics().rect(0, 0, 200, 200).fill(0x000000); const maskedContainer = new Container(); maskedContainer.setMask({ mask: inverseMask, inverse: true }); maskedContainer.addChild(sprite); stage.addChild(inverseMask); stage.addChild(maskedContainer); ``` ### Notes on Masking - The mask is **not rendered**; it's used only to define the visible area. However, it must be added to the display list. - Only one mask can be assigned per object. - For advanced blending, use **alpha masks** or **filters** (covered in later guides). ## Filters Another common use for Container objects is as hosts for filtered content. Filters are an advanced, WebGL/WebGPU-only feature that allows PixiJS to perform per-pixel effects like blurring and displacements. By setting a filter on a Container, the area of the screen the Container encompasses will be processed by the filter after the Container's contents have been rendered. ```ts const container = new Container(); const sprite = new Sprite(texture); const filter = new BlurFilter({ strength: 8, quality: 4, kernelSize: 5 }); container.filters = [filter]; container.addChild(sprite); ``` :::info Filters should be used somewhat sparingly. They can slow performance and increase memory usage if used too often in a scene. ::: Below are list of filters available by default in PixiJS. There is, however, a community repository with [many more filters](https://github.com/pixijs/filters). | Filter | Description | | ------------------ | ------------------------------------------------------------------------------------------------------------- | | AlphaFilter | Similar to setting `alpha` property, but flattens the Container instead of applying to children individually. | | BlurFilter | Apply a blur effect | | ColorMatrixFilter | A color matrix is a flexible way to apply more complex tints or color transforms (e.g., sepia tone). | | DisplacementFilter | Displacement maps create visual offset pixels, for instance creating a wavy water effect. | | NoiseFilter | Create random noise (e.g., grain effect). | Under the hood, each Filter we offer out of the box is written in both glsl (for WebGL) and wgsl (for WebGPU). This means all filters should work on both renderers. ## Tinting You can tint any scene object by setting the `tint` property. It modifies the color of the rendered pixels, similar to multiplying a tint color over the object. ```ts const sprite = new Sprite(texture); sprite.tint = 0xff0000; // Red tint sprite.tint = 'red'; // Red tint ``` The `tint` is inherited by child objects unless they specify their own. If only part of your scene should be tinted, place it in a separate container. A value of `0xFFFFFF` disables tinting. ```ts const sprite = new Sprite(texture); sprite.tint = 0x00ff00; // Green tint sprite.tint = 0xffffff; // No tint ``` PixiJS supports a variety of color formats and you can find more information from the [Color documentation](../color.md). ## Blend Modes Blend modes determine how colors of overlapping objects are combined. PixiJS supports a variety of blend modes, including: - `normal`: Default blend mode. - `add`: Adds the colors of the source and destination pixels. - `multiply`: Multiplies the colors of the source and destination pixels. - `screen`: Inverts the colors, multiplies them, and inverts again. We also support may more advanced blend modes, such as `subtract`, `difference`, and `overlay`. You can find the full list of blend modes in the [Blend Modes documentation](../filters.md#advanced-blend-modes). ```ts const sprite = new Sprite(texture); sprite.blendMode = 'multiply'; // Multiply blend mode ``` ## Interaction PixiJS provides a powerful interaction system that allows you to handle user input events like clicks/hovers. To enable interaction on a scene object, can be as simple as setting its `interactive` property to `true`. ```ts const sprite = new Sprite(texture); sprite.interactive = true; sprite.on('click', (event) => { console.log('Sprite clicked!', event); }); ``` We have a detailed guide on [Interaction](../events.md) that covers how to set up and manage interactions, including hit testing, pointer events, and more. We highly recommend checking it out. ## Using `onRender` The `onRender` callback allows you to run logic every frame when a scene object is rendered. This is useful for lightweight animation and update logic: ```ts const container = new Container(); container.onRender = () => { container.rotation += 0.01; }; ``` Note: In PixiJS v8, this replaces the common v7 pattern of overriding `updateTransform`, which no longer runs every frame. The `onRender` function is registered with the render group the container belongs to. To remove the callback: ```ts container.onRender = null; ``` --- ## API Reference - [Overview](https://pixijs.download/release/docs/scene.html) - [Container](https://pixijs.download/release/docs/scene.Container.html) - [ParticleContainer](https://pixijs.download/release/docs/scene.ParticleContainer.html) - [Sprite](https://pixijs.download/release/docs/scene.Sprite.html) - [TilingSprite](https://pixijs.download/release/docs/scene.TilingSprite.html) - [NineSliceSprite](https://pixijs.download/release/docs/scene.NineSliceSprite.html) - [Graphics](https://pixijs.download/release/docs/scene.Graphics.html) - [Mesh](https://pixijs.download/release/docs/scene.Mesh.html) - [Text](https://pixijs.download/release/docs/scene.Text.html) - [Bitmap Text](https://pixijs.download/release/docs/scene.BitmapText.html) - [HTMLText](https://pixijs.download/release/docs/scene.HTMLText.html) --- ## Mesh # Mesh PixiJS v8 offers a powerful `Mesh` system that provides full control over geometry, UVs, indices, shaders, and WebGL/WebGPU state. Meshes are ideal for custom rendering effects, advanced distortion, perspective manipulation, or performance-tuned rendering pipelines. ```ts import { Texture, Mesh, MeshGeometry, Shader } from 'pixi.js'; const geometry = new MeshGeometry({ positions: new Float32Array([0, 0, 100, 0, 100, 100, 0, 100]), uvs: new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]), indices: new Uint32Array([0, 1, 2, 0, 2, 3]), }); const shader = Shader.from({ gl: { vertex: ` attribute vec2 aPosition; attribute vec2 aUV; varying vec2 vUV; void main() { gl_Position = vec4(aPosition / 100.0 - 1.0, 0.0, 1.0); vUV = aUV; } `, fragment: ` precision mediump float; varying vec2 vUV; uniform sampler2D uSampler; void main() { gl_FragColor = texture2D(uSampler, vUV); } `, }, resources: { uSampler: Texture.from('image.png').source, }, }); const mesh = new Mesh({ geometry, shader }); app.stage.addChild(mesh); ``` ## **What Is a Mesh?** A mesh is a low-level rendering primitive composed of: - **Geometry**: Vertex positions, UVs, indices, and other attributes - **Shader**: A GPU program that defines how the geometry is rendered - **State**: GPU state configuration (e.g. blending, depth, stencil) With these elements, you can build anything from simple quads to curved surfaces and procedural effects. ## **MeshGeometry** All meshes in PixiJS are built using the `MeshGeometry` class. This class allows you to define the vertex positions, UV coordinates, and indices that describe the mesh's shape and texture mapping. ```ts const geometry = new MeshGeometry({ positions: Float32Array, // 2 floats per vertex uvs: Float32Array, // matching number of floats indices: Uint32Array, // 3 indices per triangle topology: 'triangle-list', }); ``` You can access and modify buffers directly: ```ts geometry.positions[0] = 50; geometry.uvs[0] = 0.5; geometry.indices[0] = 1; ``` ## Built-in Mesh Types ### MeshSimple A minimal wrapper over `Mesh` that accepts vertex, UV, and index arrays directly. Suitable for fast static or dynamic meshes. ```ts const mesh = new MeshSimple({ texture: Texture.from('image.png'), vertices: new Float32Array([0, 0, 100, 0, 100, 100, 0, 100]), uvs: new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]), indices: new Uint32Array([0, 1, 2, 0, 2, 3]), }); ``` - Use `autoUpdate = true` to update geometry per frame. - Access `mesh.vertices` to read/write data. ### MeshRope Bends a texture along a series of control points, often used for trails, snakes, and animated ribbons. ```ts const points = [new Point(0, 0), new Point(100, 0), new Point(200, 50)]; const rope = new MeshRope({ texture: Texture.from('snake.png'), points, textureScale: 1, // optional }); ``` - `textureScale > 0` repeats texture; `0` stretches it. - `autoUpdate = true` re-evaluates geometry each frame. ### MeshPlane A flexible subdivided quad mesh, suitable for distortion or grid-based warping. ```ts const plane = new MeshPlane({ texture: Texture.from('image.png'), verticesX: 10, verticesY: 10, }); ``` - Automatically resizes on texture update when `autoResize = true`. ### PerspectiveMesh A special subclass of `MeshPlane` that applies perspective correction by transforming the UVs. ```ts const mesh = new PerspectiveMesh({ texture: Texture.from('image.png'), verticesX: 20, verticesY: 20, x0: 0, y0: 0, x1: 300, y1: 30, x2: 280, y2: 300, x3: 20, y3: 280, }); ``` - Set corner coordinates via `setCorners(...)`. - Ideal for emulating 3D projection in 2D. --- ## **API Reference** - [Mesh](https://pixijs.download/release/docs/scene.Mesh.html) - [MeshGeometry](https://pixijs.download/release/docs/scene.MeshGeometry.html) - [MeshSimple](https://pixijs.download/release/docs/scene.MeshSimple.html) - [MeshRope](https://pixijs.download/release/docs/scene.MeshRope.html) - [MeshPlane](https://pixijs.download/release/docs/scene.MeshPlane.html) - [PerspectiveMesh](https://pixijs.download/release/docs/scene.PerspectiveMesh.html) - [Shader](https://pixijs.download/release/docs/rendering.Shader.html) - [Texture](https://pixijs.download/release/docs/rendering.Texture.html) --- ## NineSlice Sprite # NineSlice Sprite `NineSliceSprite` is a specialized type of `Sprite` that allows textures to be resized while preserving the corners and edges. It is particularly useful for building scalable UI elements like buttons, panels, or windows with rounded or decorated borders. ```ts import { NineSliceSprite, Texture } from 'pixi.js'; const nineSlice = new NineSliceSprite({ texture: Texture.from('button.png'), leftWidth: 15, topHeight: 15, rightWidth: 15, bottomHeight: 15, width: 200, height: 80, }); app.stage.addChild(nineSlice); ``` You can also pass just a texture, and the slice values will fall back to defaults or be inferred from the texture’s `defaultBorders`. ## **How NineSlice Works** Here’s how a nine-slice texture is divided: ```js A B +---+----------------------+---+ C | 1 | 2 | 3 | +---+----------------------+---+ | | | | | 4 | 5 | 6 | | | | | +---+----------------------+---+ D | 7 | 8 | 9 | +---+----------------------+---+ Areas: - 1, 3, 7, 9: Corners (remain unscaled) - 2, 8: Top/Bottom center (stretched horizontally) - 4, 6: Left/Right center (stretched vertically) - 5: Center (stretched in both directions) ``` This ensures that decorative corners are preserved and the center content can scale as needed. ## **Width and Height Behavior** Setting `.width` and `.height` on a `NineSliceSprite` updates the **geometry vertices**, not the texture UVs. This allows the texture to repeat or stretch correctly based on the slice regions. This also means that the `width` and `height` properties are not the same as the `scale` properties. ```ts // The texture will stretch to fit the new dimensions nineSlice.width = 300; nineSlice.height = 100; // The nine-slice will increase in size uniformly nineSlice.scale.set(2); // Doubles the size ``` ### **Original Width and Height** If you need to know the original size of the nine-slice, you can access it through the `originalWidth` and `originalHeight` properties. These values are set when the `NineSliceSprite` is created and represent the dimensions of the texture before any scaling or resizing is applied. ```ts console.log(nineSlice.originalWidth); console.log(nineSlice.originalHeight); ``` ## **Dynamic Updates** You can change slice dimensions or size at runtime: ```ts nineSlice.leftWidth = 20; nineSlice.topHeight = 25; ``` Each setter triggers a geometry update to reflect the changes. --- ## **API Reference** - [NineSliceSprite](https://pixijs.download/release/docs/scene.NineSliceSprite.html) --- ## Particle Container # Particle Container PixiJS v8 introduces a high-performance particle system via the `ParticleContainer` and `Particle` classes. Designed for rendering vast numbers of lightweight visuals—like sparks, bubbles, bunnies, or explosions—this system provides raw speed by stripping away all non-essential overhead. :::warning **Experimental API Notice** The Particle API is stable but **experimental**. Its interface may evolve in future PixiJS versions. We welcome feedback to help guide its development. ::: ```ts import { ParticleContainer, Particle, Texture } from 'pixi.js'; const texture = Texture.from('bunny.png'); const container = new ParticleContainer({ dynamicProperties: { position: true, // default scale: false, rotation: false, color: false, }, }); for (let i = 0; i < 100000; i++) { const particle = new Particle({ texture, x: Math.random() * 800, y: Math.random() * 600, }); container.addParticle(particle); } app.stage.addChild(container); ``` ## **Why Use ParticleContainer?** - **Extreme performance**: Render hundreds of thousands or even millions of particles with high FPS. - **Lightweight design**: Particles are more efficient than `Sprite`, lacking extra features like children, events, or filters. - **Fine-grained control**: Optimize rendering by declaring which properties are dynamic (updated every frame) or static (set once). ### **Performance Tip: Static vs. Dynamic** - **Dynamic properties** are uploaded to the GPU every frame. - **Static properties** are uploaded only when `update()` is called. Declare your needs explicitly: ```ts const container = new ParticleContainer({ dynamicProperties: { position: true, rotation: true, scale: false, color: false, }, }); ``` If you later modify a static property or the particle list, you must call: ```ts container.update(); ``` ## **Limitations and API Differences** `ParticleContainer` is designed for speed and simplicity. As such, it doesn't support the full `Container` API: ### ❌ Not Available: - `addChild()`, `removeChild()` - `getChildAt()`, `setChildIndex()` - `swapChildren()`, `reparentChild()` ### ✅ Use Instead: - `addParticle(particle)` - `removeParticle(particle)` - `removeParticles(beginIndex, endIndex)` - `addParticleAt(particle, index)` - `removeParticleAt(index)` These methods operate on the `.particleChildren` array and maintain the internal GPU buffers correctly. ## **Creating a Particle** A `Particle` supports key display properties, and is far more efficient than `Sprite`. ### **Particle Example** ```ts const particle = new Particle({ texture: Texture.from('spark.png'), x: 200, y: 100, scaleX: 0.8, scaleY: 0.8, rotation: Math.PI / 4, tint: 0xff0000, alpha: 0.5, }); ``` You can also use the shorthand: ```ts const particle = new Particle(Texture.from('spark.png')); ``` --- ## **API Reference** - [ParticleContainer](https://pixijs.download/release/docs/scene.ParticleContainer.html) - [Particle](https://pixijs.download/release/docs/scene.Particle.html) --- ## Sprite # Sprite Sprites are the foundational visual elements in PixiJS. They represent a single image to be displayed on the screen. Each [Sprite](https://pixijs.download/release/docs/scene.Sprite.html) contains a [Texture](https://pixijs.download/release/docs/rendering.Texture.html) to be drawn, along with all the transformation and display state required to function in the scene graph. ```ts import { Assets, Sprite } from 'pixi.js'; const texture = await Assets.load('path/to/image.png'); const sprite = new Sprite(texture); sprite.anchor.set(0.5); sprite.position.set(100, 100); sprite.scale.set(2); sprite.rotation = Math.PI / 4; // Rotate 45 degrees ``` ## Updating the Texture If you change the texture of a sprite, it will automatically: - Rebind listeners for texture updates - Recalculate width/height if set so that the visual size remains the same - Trigger a visual update ```ts const texture = Assets.get('path/to/image.png'); sprite.texture = texture; ``` ## **Scale vs Width/Height** Sprites inherit `scale` from `Container`, allowing for percentage-based resizing: ```ts sprite.scale.set(2); // Double the size ``` Sprites also have `width` and `height` properties that act as _convenience setters_ for `scale`, based on the texture’s dimensions: ```ts sprite.width = 100; // Automatically updates scale.x // sets: sprite.scale.x = 100 / sprite.texture.orig.width; ``` --- ## API Reference - [Sprite](https://pixijs.download/release/docs/scene.Sprite.html) - [Texture](https://pixijs.download/release/docs/rendering.Texture.html) - [Assets](https://pixijs.download/release/docs/assets.Assets.html) --- ## Bitmap Text # Bitmap Text `BitmapText` is a high-performance text rendering solution in PixiJS. Unlike the `Text` class, which rasterizes each string into a new texture, `BitmapText` draws characters from a pre-generated texture atlas. This design allows you to render tens of thousands of text objects with minimal overhead. ```ts import { Assets, BitmapText } from 'pixi.js'; await Assets.load('fonts/MyFont.fnt'); const text = new BitmapText({ text: 'Loaded font!', style: { fontFamily: 'MyFont', fontSize: 32, fill: '#ffcc00', }, }); ``` ## **Why Use `BitmapText`?** - ✅ **Fast rendering**: Perfect for HUDs, score counters, timers, etc. - ✅ **No per-frame rasterization**: Text changes are cheap. - ✅ **Efficient memory usage**: Shares glyph textures across all instances. - ✅ **Supports MSDF/SDF fonts**: Crisp scaling without blurring. **Ideal use cases**: - Frequently updating text - Large numbers of text instances - High-performance or mobile projects ## **How to Load and Use Bitmap Fonts** ### Font Loading PixiJS supports AngelCode BMFont format and MSDF-compatible `.fnt` and `.xml` files. You can load these files using the `Assets` API. Once loaded, you can create a `BitmapText` instance with the loaded font using the `fontFamily` property. ```ts import { Assets, BitmapText } from 'pixi.js'; await Assets.load('fonts/MyFont.fnt'); const text = new BitmapText({ text: 'Loaded font!', style: { fontFamily: 'MyFont', fontSize: 32, fill: '#ffcc00', }, }); ``` ### MSDF and SDF Fonts PixiJS supports **MSDF** (multi-channel signed distance field) and **SDF** formats for crisp, resolution-independent text. These fonts remain sharp at any size and scale. You can generate MSDF/SDF fonts using tools like [AssetPack](https://pixijs.io/assetpack/) which can take a `.ttf` or `.otf` font and generate a bitmap font atlas with MSDF/SDF support. Using MSDF/SDF fonts is similar to using regular bitmap fonts and just requires you to load the appropriate font file: ```ts import { Assets, BitmapText } from 'pixi.js'; await Assets.load('fonts/MyMSDFFont.fnt'); const text = new BitmapText({ text: 'Loaded MSDF font!', style: { fontFamily: 'MyMSDFFont', }, }); ``` # **Limitations and Caveats** ### ❌ Cannot Update Resolution `BitmapText.resolution` is not mutable. It must be handled by the `BitmapFont` ```ts text.resolution = 2; // ⚠️ [BitmapText] dynamically updating the resolution is not supported. ``` ### ⚠️ Large Character Sets Not Practical Bitmap fonts are limited by texture size. CJK or emoji-rich sets may require too much memory. Use `Text` or `HTMLText` for dynamic internationalization or emoji support. --- ## **API Reference** - [BitmapText](https://pixijs.download/release/docs/scene.BitmapText.html) - [BitmapFont](https://pixijs.download/release/docs/text.BitmapFont.html) - [Assets](https://pixijs.download/release/docs/assets.Assets.html) - [TextStyle](https://pixijs.download/release/docs/text.TextStyle.html) - [FillStyle](https://pixijs.download/release/docs/scene.FillStyle.html) - [StrokeStyle](https://pixijs.download/release/docs/scene.StrokeStyle.html) --- ## Text (Canvas) # Text (Canvas) The `Text` class in PixiJS allows you to render styled, dynamic strings as display objects in your scene. Under the hood, PixiJS rasterizes the text using the browser’s canvas text API, then converts that into a texture. This makes `Text` objects behave like sprites: they can be moved, rotated, scaled, masked, and rendered efficiently. ```ts import { Text, TextStyle, Assets } from 'pixi.js'; // Load font before use await Assets.load({ src: 'my-font.woff2', data: { family: 'MyFont', // optional } }); const myText = new Text({ text: 'Hello PixiJS!', style: { fill: '#ffffff', fontSize: 36, fontFamily: 'MyFont', } anchor: 0.5 }); app.stage.addChild(myText); ``` ## Text Styling The `TextStyle` class allows you to customize the appearance of your text. You can set properties like: - `fontFamily` - `fontSize` - `fontWeight` - `fill` (color) - `align` - `stroke` (outline) See the guide on [TextStyle](./style.md) for more details. ## **Changing Text Dynamically** You can update the text string or its style at runtime: ```ts text.text = 'Updated!'; text.style.fontSize = 40; // Triggers re-render ``` :::warning Changing text or style re-rasterizes the object. Avoid doing this every frame unless necessary. ::: ## Text Resolution The `resolution` property of the `Text` class determines the pixel density of the rendered text. By default, it uses the resolution of the renderer. However, you can set text resolution independently from the renderer for sharper text, especially on high-DPI displays. ```ts const myText = new Text('Hello', { resolution: 2, // Higher resolution for sharper text }); // change resolution myText.resolution = 1; // Reset to default ``` ## Font Loading PixiJS supports loading custom fonts via the `Assets` API. It supports many of the common font formats: - `woff` - `woff2` - `ttf` - `otf` It is recommended to use `woff2` for the best performance and compression. ```js await Assets.load({ src: 'my-font.woff2', data: {}, }); ``` Below is a list of properties you can pass in the `data` object when loading a font: | Property | Description | | ------------------- | ------------------------------------------------------- | | **family** | The font family name. | | **display** | The display property of the FontFace interface. | | **featureSettings** | The featureSettings property of the FontFace interface. | | **stretch** | The stretch property of the FontFace interface. | | **style** | The style property of the FontFace interface. | | **unicodeRange** | The unicodeRange property of the FontFace interface. | | **variant** | The variant property of the FontFace interface. | | **weights** | The weights property of the FontFace interface. | --- ## API Reference - [Text](https://pixijs.download/release/docs/scene.Text.html) - [TextStyle](https://pixijs.download/release/docs/text.TextStyle.html) - [FillStyle](https://pixijs.download/release/docs/scene.FillStyle.html) - [StrokeStyle](https://pixijs.download/release/docs/scene.StrokeStyle.html) --- ## HTML Text # HTML Text `HTMLText` enables styled, formatted HTML strings to be rendered as part of the PixiJS scene graph. It uses an SVG `` to embed browser-native HTML inside your WebGL canvas. This makes it ideal for rendering complex typography, inline formatting, emojis, and layout effects that are hard to replicate using traditional canvas-rendered text. ```ts import { HTMLText } from 'pixi.js'; const html = new HTMLText({ text: 'Hello PixiJS!', style: { fontFamily: 'Arial', fontSize: 24, fill: '#ff1010', align: 'center', }, }); app.stage.addChild(html); ``` ## **Why Use `HTMLText`?** - ✅ Supports inline tags like `, `, ``, etc. - ✅ Compatible with emojis, Unicode, and RTL text - ✅ Fine-grained layout control via CSS - ✅ Tag-based style overrides (`, `, etc.) ## **Async Rendering Behavior** HTML text uses SVG `` to draw HTML inside the canvas. As a result: - Rendering occurs **asynchronously**. Typically after the next frame. - Text content is not immediately visible after instantiation. ## **Styling HTMLText** `HTMLTextStyle` extends `TextStyle` and adds: - **HTML-aware tag-based styles** via `tagStyles` - **CSS override support** via `cssOverrides` ```ts const fancy = new HTMLText({ text: 'Red, Blue', style: { fontFamily: 'DM Sans', fontSize: 32, fill: '#ffffff', tagStyles: { red: { fill: 'red' }, blue: { fill: 'blue' }, }, }, }); ``` ### **CSS Overrides** You can apply CSS styles to the text using the `cssOverrides` property. This allows you to set properties like `text-shadow`, `text-decoration`, and more. ```ts fancy.style.addOverride('text-shadow: 2px 2px 4px rgba(0,0,0,0.5)'); ``` --- ## **API Reference** - [HTMLText](https://pixijs.download/release/docs/scene.HTMLText.html) - [HTMLTextStyle](https://pixijs.download/release/docs/text.HTMLTextStyle.html) --- ## Text # Text Text is essential in games and applications, and PixiJS provides three different systems to cover different needs: - **`Text`**: High-quality, styled raster text - **`BitmapText`**: Ultra-fast, GPU-optimized bitmap fonts - **`HTMLText`**: Richly formatted HTML inside the Pixi scene Each approach has tradeoffs in fidelity, speed, and flexibility. Let’s look at when and how to use each one. ## `Text`: Rich Dynamic Text with Styles The `Text` class renders using the browser's native text engine, and then converts the result into a texture. This allows for: - Full CSS-like font control - Drop shadows, gradients, and alignment - Support for dynamic content and layout **Use it when**: - You need detailed font control - Text changes occasionally - Fidelity is more important than speed **Avoid when**: - You're changing text every frame - You need to render hundreds of instances --- ## `BitmapText`: High-Performance Glyph Rendering `BitmapText` uses a pre-baked bitmap font image for glyphs, bypassing font rasterization entirely. This gives: - High rendering speed - Ideal for thousands of changing labels - Low memory usage **Use it when**: - You need to render lots of dynamic text - You prioritize performance over styling - You predefine the characters and styles **Avoid when**: - You need fine-grained style control - You change fonts or font sizes frequently To use `BitmapText`, you must first register a bitmap font via: - `BitmapFont.from(...)` to create on the fly, or - `Assets.load(...)` if using a 3rd-party BMFont or AngelCode export --- ## `HTMLText`: Styled HTML Inside the Scene `HTMLText` lets you render actual HTML markup in your PixiJS scene. This allows: - `, `, `` style formatting - Fine layout and text flow - Emoji, RTL, links, and more **Use it when**: - You want complex HTML-style markup - Static or semi-dynamic content - Your users expect "document-like" layout **Avoid when**: - You need pixel-perfect performance - You're rendering hundreds of text blocks - You need GPU text effects ## Next Steps Each text type has a corresponding guide that covers setup, font loading, style options, and limitations in more detail: - [Text Guide →](./canvas.md) - [BitmapText Guide →](./bitmap.md) - [HTMLText Guide →](./html.md) --- ## API Reference - [Text](https://pixijs.download/release/docs/scene.Text.html) - [BitmapText](https://pixijs.download/release/docs/scene.BitmapText.html) - [HTMLText](https://pixijs.download/release/docs/scene.HTMLText.html) - [TextStyle](https://pixijs.download/release/docs/text.TextStyle.html) - [BitmapFont](https://pixijs.download/release/docs/text.BitmapFont.html) - [FillStyle](https://pixijs.download/release/docs/scene.FillStyle.html) - [StrokeStyle](https://pixijs.download/release/docs/scene.StrokeStyle.html) --- ## SplitText & SplitBitmapText # SplitText & SplitBitmapText The `SplitText` and `SplitBitmapText` classes let you break a string into individual lines, words, and characters—each as its own display object—unlocking rich, per-segment animations and advanced text layout effects. These classes work just like regular `Text` or `BitmapText`, but provide fine-grained control over every part of your text. :::warning **Experimental:** These features are new and may evolve in future versions. ::: --- ## What Are SplitText & SplitBitmapText? Both `SplitText` and `SplitBitmapText` extend PixiJS containers, generating nested display objects for each line, word, and character of your string. The difference is in the underlying text rendering: | Class | Base Type | Best For | | ----------------- | ------------ | ----------------------------- | | `SplitText` | `Text` | Richly styled text | | `SplitBitmapText` | `BitmapText` | High-performance dynamic text | **Perfect for:** - Per-character or per-word animations - Line-based entrance or exit effects - Interactive text elements - Complex animated typography --- ## Basic Usage ### SplitText Example ```ts import { SplitText } from 'pixi.js'; const text = new SplitText({ text: 'Hello World', style: { fontSize: 32, fill: 0xffffff }, // Optional: Anchor points (0-1 range) lineAnchor: 0.5, // Center lines wordAnchor: { x: 0, y: 0.5 }, // Left-center words charAnchor: { x: 0.5, y: 1 }, // Bottom-center characters autoSplit: true, }); app.stage.addChild(text); ``` ### SplitBitmapText Example ```ts import { SplitBitmapText, BitmapFont } from 'pixi.js'; // Ensure your bitmap font is installed BitmapFont.install({ name: 'Game Font', style: { fontFamily: 'Arial', fontSize: 32 }, }); const text = new SplitBitmapText({ text: 'High Performance', style: { fontFamily: 'Game Font', fontSize: 32 }, autoSplit: true, }); app.stage.addChild(text); ``` --- ## Accessing Segments Both classes provide convenient segment arrays: ```ts console.log(text.lines); // Array of line containers console.log(text.words); // Array of word containers console.log(text.chars); // Array of character display objects ``` Each line contains its words, and each word contains its characters. --- ## Anchor Points Explained Segment anchors control the origin for transformations like rotation or scaling. Normalized range: `0` (start) to `1` (end) | Anchor | Meaning | | --------- | ------------ | | `0,0` | Top-left | | `0.5,0.5` | Center | | `1,1` | Bottom-right | **Example:** ```ts const text = new SplitText({ text: 'Animate Me', lineAnchor: { x: 1, y: 0 }, // Top-right lines wordAnchor: 0.5, // Center words charAnchor: { x: 0, y: 1 }, // Bottom-left characters }); ``` --- ## Animation Example (with GSAP) ```ts import { gsap } from 'gsap'; const text = new SplitBitmapText({ text: 'Split and Animate', style: { fontFamily: 'Game Font', fontSize: 48 }, }); app.stage.addChild(text); // Animate characters one by one text.chars.forEach((char, i) => { gsap.from(char, { alpha: 0, delay: i * 0.05, }); }); // Animate words with scaling text.words.forEach((word, i) => { gsap.to(word.scale, { x: 1.2, y: 1.2, yoyo: true, repeat: -1, delay: i * 0.2, }); }); ``` --- ## Creating from Existing Text Convert existing text objects into segmented versions: ```ts import { Text, SplitText, BitmapText, SplitBitmapText } from 'pixi.js'; const basicText = new Text({ text: 'Standard Text', style: { fontSize: 32 }, }); const splitText = SplitText.from(basicText); const bitmapText = new BitmapText({ text: 'Bitmap Example', style: { fontFamily: 'Game Font', fontSize: 32 }, }); const splitBitmap = SplitBitmapText.from(bitmapText); ``` --- ## Configuration Options Shared options for both classes: | Option | Description | Default | | ------------ | --------------------------------------------------- | ---------- | | `text` | String content to render and split | _Required_ | | `style` | Text style configuration (varies by text type) | `{}` | | `autoSplit` | Auto-update segments when text or style changes | `true` | | `lineAnchor` | Anchor for line containers (`number` or `{x, y}`) | `0` | | `wordAnchor` | Anchor for word containers (`number` or `{x, y}`) | `0` | | `charAnchor` | Anchor for character objects (`number` or `{x, y}`) | `0` | --- ## Global Defaults Change global defaults for each class: ```ts SplitText.defaultOptions = { lineAnchor: 0.5, wordAnchor: { x: 0, y: 0.5 }, charAnchor: { x: 0.5, y: 1 }, }; SplitBitmapText.defaultOptions = { autoSplit: false, }; ``` --- ## Limitations ⚠️ **Character Spacing:** Splitting text into individual objects removes browser or font kerning. Expect slight differences in spacing compared to standard `Text`. --- ## Performance Notes Splitting text creates additional display objects. For simple static text, a regular `Text` object is more efficient. Use `SplitText` when you need: - Per-segment animations - Interactive or responsive text effects - Complex layouts The same performance limitations outlined [here](./index.md) apply for these classes as well. --- ## API Reference - [Text](https://pixijs.download/release/docs/scene.Text.html) - [TextStyle](https://pixijs.download/release/docs/text.TextStyle.html) - [BitmapFont](https://pixijs.download/release/docs/text.BitmapFont.html) - [SplitText](https://pixijs.download/release/docs/text.SplitText.html) - [SplitBitmapText](https://pixijs.download/release/docs/text.SplitBitmapText.html) --- ## Text Style # Text Style The `TextStyle` class encapsulates all visual styling properties for text. You can define colors, font families, stroke, shadows, alignment, line spacing, word wrapping, and more. A `TextStyle` instance can be reused across multiple `Text` objects, making your code cleaner and improving memory efficiency. ```ts import { TextStyle } from 'pixi.js'; const style = new TextStyle({ fontFamily: 'Arial', fontSize: 30, fill: '#ffffff', stroke: '#000000', strokeThickness: 3, dropShadow: { color: '#000000', blur: 5, distance: 4, angle: Math.PI / 4, alpha: 0.5, }, }); const label = new Text({ text: 'Score: 1234', style, }); ``` ## Fill and Stroke Using fills and strokes are identical to that of the `Graphics` class. You can find more details about them in the [Graphics Fills](../graphics/graphics-fill.md) section. ```ts // Using a number const fill = 0xff0000; // Using a hex string const fill = '#ff0000'; // Using an array const fill = [255, 0, 0]; // Using a Color object const color = new Color(); const obj4 = color; // Using a gradient const fill = new FillGradient({ type: 'linear', colorStops: [ { offset: 0, color: 'yellow' }, { offset: 1, color: 'green' }, ], }); // Using a pattern const txt = await Assets.load('https://pixijs.com/assets/bg_scene_rotate.jpg'); const fill = new FillPattern(txt, 'repeat'); // Use the fill in a TextStyle const style = new TextStyle({ fontSize: 48, fill: fill, stroke: { fill, width: 4, }, }); ``` ## Drop Shadow In v8 `dropShadow` and its properties are now objects. To update a drop shadow, you can set the properties directly on the `dropShadow` object. ```ts const style = new TextStyle({ dropShadow: { color: '#000000', alpha: 0.5, angle: Math.PI / 4, blur: 5, distance: 4, }, }); style.dropShadow.color = '#ff0000'; // Change shadow color ``` --- ## **API Reference** - [TextStyle](https://pixijs.download/release/docs/text.TextStyle.html) - [Text](https://pixijs.download/release/docs/scene.Text.html) - [BitmapText](https://pixijs.download/release/docs/scene.BitmapText.html) - [HTMLText](https://pixijs.download/release/docs/scene.HTMLText.html) - [FillStyle](https://pixijs.download/release/docs/scene.FillStyle.html) - [StrokeStyle](https://pixijs.download/release/docs/scene.StrokeStyle.html) --- ## Tiling Sprite # Tiling Sprite A `TilingSprite` is a high-performance way to render a repeating texture across a rectangular area. Instead of stretching the texture, it repeats (tiles) it to fill the given space, making it ideal for backgrounds, parallax effects, terrain, and UI panels. ```ts import { TilingSprite, Texture } from 'pixi.js'; const tilingSprite = new TilingSprite({ texture: Texture.from('assets/tile.png'), width: 400, height: 300, }); app.stage.addChild(tilingSprite); ``` ## What is a TilingSprite? - It draws a texture repeatedly across a defined area. - The texture is not scaled by default unless you adjust `tileScale`. - The sprite's overall `width` and `height` determine how much area the tiles fill, independent of the texture's native size. - The pattern's offset, scale, and rotation can be controlled independently of the object's transform. ## Key Properties | Property | Description | | ---------------------- | ---------------------------------------------------------------------- | | `texture` | The texture being repeated | | `tilePosition` | `ObservablePoint` controlling offset of the tiling pattern | | `tileScale` | `ObservablePoint` controlling scaling of each tile | | `tileRotation` | Number controlling the rotation of the tile pattern | | `width` / `height` | The size of the area to be filled by tiles | | `anchor` | Adjusts origin point of the TilingSprite | | `applyAnchorToTexture` | If `true`, the anchor affects the starting point of the tiling pattern | | `clampMargin` | Margin adjustment to avoid edge artifacts (default: `0.5`) | ### Width & Height vs Scale Unlike `Sprite`, setting `width` and `height` in a `TilingSprite` directly changes the visible tiling area. It **does not affect the scale** of the tiles. ```ts // Makes the tiling area bigger tilingSprite.width = 800; tilingSprite.height = 600; // Keeps tiles the same size, just tiles more of them ``` To scale the tile pattern itself, use `tileScale`: ```ts // Makes each tile appear larger tilingSprite.tileScale.set(2, 2); ``` ### Anchor and applyAnchorToTexture - `anchor` sets the pivot/origin point for positioning the TilingSprite. - If `applyAnchorToTexture` is `true`, the anchor also affects where the tile pattern begins. - By default, the tile pattern starts at (0, 0) in local space regardless of anchor. --- ## API Reference - [TilingSprite](https://pixijs.download/release/docs/scene.TilingSprite.html) - [Texture](https://pixijs.download/release/docs/rendering.Texture.html) --- ## Documentation for LLMs # Documentation for LLMs PixiJS supports the [`llms.txt`](https://llmstxt.org/) convention for making documentation accessible to large language models (LLMs) and the applications that utilize them. ## Available Files We provide several documentation files at different compression levels to accommodate various context window sizes: - [`/llms.txt`](https://pixijs.com/llms.txt) - An index of available documentation files - [`/llms-full.txt`](https://pixijs.com/llms-full.txt) - Complete PixiJS API documentation including all classes, methods, and examples - [`/llms-medium.txt`](https://pixijs.com/llms-medium.txt) - Compressed documentation optimized for medium context windows These files are automatically generated and updated daily from our TypeScript definition files and documentation sources. --- ## Sponsor PixiJS # Sponsor PixiJS PixiJS is an MIT-licensed open source project and completely free to use. It's trusted by companies and developers around the world to power high-performance graphics on the web. The enormous effort required to maintain the project, evolve its feature set, and support its growing ecosystem is only made possible through the financial support of our sponsors. --- ## How to Sponsor You can sponsor PixiJS through [GitHub Sponsors](https://github.com/sponsors/pixijs) or [OpenCollective](https://opencollective.com/pixijs). Both one-time and monthly recurring contributions are welcome. Sponsors with recurring support are eligible for logo placements and other benefits based on their [sponsorship tier](#sponsorship-tiers). --- ## Sponsoring PixiJS as a Business Supporting PixiJS isn’t just good for open source—it’s good business. Whether you're building games, creative tools, or data visualizations, sponsoring PixiJS gives you: - **Visibility and brand recognition** across our website, documentation, showcase, and release posts. - **Direct investment in the tools you rely on**, helping ensure continued updates and a healthy, well-maintained codebase. - **Help shape the roadmap** by supporting the features and improvements that matter most to your product. Sponsorship is also an excellent way to attract developer talent. By contributing to a community-driven project, your brand demonstrates a commitment to quality engineering and open collaboration. If your product or service depends on PixiJS, sponsoring helps ensure its long-term stability—and gives you a voice in prioritizing features and fixes. --- ## Sponsoring PixiJS as an Individual If you’ve enjoyed using PixiJS in your personal projects, consider donating as a way to give back. Even small contributions help and are a meaningful sign of support! Even better, encourage your employer to become a business sponsor. This has a far greater impact and helps ensure PixiJS continues to grow in ways that benefit everyone. --- ## Sponsorship Tiers We offer tiered sponsorships to give our partners valuable exposure and benefits in return for their support: | Tier | Monthly | Benefits | | ------------ | ------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | **Bronze** | \$100 | Logo placed on our website and a link to your site | | **Silver** | \$250 | Bronze benefits Logo in our [release blog posts](https://pixijs.com/blog/8.10.0) 1 sponsored link in the showcase (always appear at the top!) | | **Gold** | \$500 | Silver benefits Logo on all documentation pages in the sidebar of our website Logo in GitHub README | | **Gold++** | \$1000 | Gold benefits 2 sponsored links in the showcase (always appear at the top!) Priority on bug fixes | | **Platinum** | \$2000 | Gold++ benefits The most prominent logo placement on our website Highest priority on bug fixes Help shape what we work on next! |