Skip to content
Development
Skill

/pixijs-rendering

Builds and debugs 2D browser game rendering with PixiJS v8: async init, Assets loading, the Container and Sprite scene graph, pointer dragging and v7-to-v8 migration. Use when a game or playable ad uses pixi.js, or on a blank canvas, clicks not firing, or drag stopping at the

From plugin
awesome-gamedev-agent-skills
8.6k79 skills

Claude Code

$ npx -y skills add gamedev-skills/awesome-gamedev-agent-skills --skill pixijs-rendering --agent claude-code

SKILL.md

pixijs-rendering.SKILL.md
name: pixijs-rendering
description: >
  Builds and debugs 2D browser game rendering with PixiJS v8: async init, Assets loading, the
  Container and Sprite scene graph, pointer dragging and v7-to-v8 migration. Use when a game or
  playable ad uses pixi.js, or on a blank canvas, clicks not firing, or drag stopping at the
  sprite edge. Not for dashboards or charts, Phaser (`phaser-core`) or 3D (`threejs-scene-setup`).

PixiJS 8 rendering for games

Sets up and structures a PixiJS **8.22** game renderer: the async `Application`, asset loading through `Assets`, the `Container` scene graph, the ticker loop, pointer events and render groups. PixiJS draws; it has no scenes, physics or input mapping. A game that wants those built in is usually better served by Phaser. With PixiJS you write the loop, the screen manager and the collision code yourself.

When to use

  • Starting a PixiJS v8 game or playable, fixing a blank canvas, structuring the display list,

loading textures and sprite sheets, animating with the ticker, or handling pointer input.

  • `package.json` depends on `pixi.js` 8.x and code imports from `'pixi.js'`.
  • Not for dashboards, charts or other non-game UIs, Phaser projects (`phaser-core`) or 3D

(`threejs-scene-setup`). PixiJS v7 code (`new Application({...})`, `Loader`, `beginFill`) must be migrated first; the Gotchas list the renames.

Workflow

1. **Read the installed version.** v8 changed boot, loading, Graphics and events, so v7 answers from memory break here. 2. **Create, then `await app.init({...})`.** The v8 constructor takes no options. Append `app.canvas` to the page. 3. **Load before you build.** `await Assets.load(...)` or a manifest bundle per screen; the returned `Texture` is what `Sprite` takes. 4. **Build the display list from `Container`s**, one per layer (world, effects, HUD). Child transforms are relative to the parent; later children draw on top. 5. **Run game logic in one ticker callback** scaled by `ticker.deltaMS / 1000` (seconds) or `ticker.deltaTime` (≈1 per 60 Hz frame). Keep rules in plain modules the callback calls. 6. **Turn on input per object** with `eventMode = 'static'`, then `on('pointerdown', ...)`. 7. **Clean up on screen changes**: `destroy()` what you remove and `Assets.unload()` what the next screen does not need. 8. **Verify** with [Verify](#verify).

Patterns

1. Async boot

import { Application, Assets, Sprite } from 'pixi.js';

const app = new Application();
await app.init({
  background: '#1099bb',
  resizeTo: window,
  antialias: true,
  // preference: 'webgpu', // a hint; default order is webgl, webgpu, canvas
});
document.body.appendChild(app.canvas);
Object.assign(window, { app }); // for the smoke check and console debugging (TS-safe)

const texture = await Assets.load('assets/bunny.png');
const bunny = new Sprite(texture);
bunny.anchor.set(0.5);
bunny.position.set(app.screen.width / 2, app.screen.height / 2);
app.stage.addChild(bunny);

2. Layers as containers

import { Container } from 'pixi.js';

const world = new Container();       // camera: move or scale this, not every sprite
const hud = new Container();         // added last, so it draws over the world
app.stage.addChild(world, hud);

world.position.set(-camera.x, -camera.y);
world.scale.set(2);

3. One game loop on the ticker

app.ticker.add((ticker) => {
  const dt = ticker.deltaMS / 1000;  // seconds since the last frame
  stepGame(state, input, dt);        // rules live in a plain module
  bunny.rotation += 1.5 * dt;
});

4. Pointer input and dragging

bunny.eventMode = 'static';          // the v8 default 'passive' ignores the object itself
bunny.cursor = 'pointer';
bunny.on('pointerdown', () => { dragging = true; });
bunny.on('pointerup', () => { dragging = false; });
bunny.on('pointerupoutside', () => { dragging = false; });
// pointermove fires only over the object in v8; globalpointermove fires on every move.
bunny.on('globalpointermove', (event) => {
  if (dragging) bunny.position.copyFrom(event.global);
});

5. Load a screen's assets by name

// addBundle works at any time; Assets.init({ manifest }) only works before the first load.
Assets.addBundle('level-1', [
  { alias: 'hero', src: 'assets/hero.png' },
  { alias: 'atlas', src: 'assets/characters.json' }, // TexturePacker JSON
]);
Assets.addBundle('level-2', [{ alias: 'boss', src: 'assets/boss.png' }]);

const level = await Assets.loadBundle('level-1');
const hero = new Sprite(level.hero);
Assets.backgroundLoadBundle('level-2'); // warm the next screen while this one plays

6. Render group for a large static layer

const background = new Container({ isRenderGroup: true });
app.stage.addChildAt(background, 0);
// Hundreds of static tiles go in here. Moving `background` stays cheap;
// adding and removing children every frame cancels the benefit.

Verify

1. `npm run build` (or `npx tsc --noEmit`) passes. 2. Smoke-test in a headless browser. With the dev server running and `window.app` exposed (Pattern 1), run `node smoke.mjs <url>` after `npm i -D playwright` and `npx playwright install chromium`. Without browser automation, ask the user to open the page and report the console output and what is drawn.

// smoke.mjs: fails on page errors; prints the backend and the display list size.
import { chromium } from 'playwright';

const browser = await chromium.launch();
const page = await browser.newPage();
const errors = [];
page.on('pageerror', (e) => errors.push(e.message));
page.on('console', (m) => { if (m.type() === 'error') errors.push(m.text()); });
await page.goto(process.argv[2]);
await page.waitForFunction(() => window.app?.renderer, null, { timeout: 15000 });
await page.waitForTimeout(1000); // let the first assets load
const state = await page.evaluate(() => ({
  backend: window.app.renderer.name,           // 'webgl', 'webgpu' or 'canvas'
  stageChildren: window.ap
Read more
@gamedev-skills
Ships withawesome-gamedev-agent-skills

<img src="docs/assets/awesome-gamedev-agent-skills-banner.png" width="820" alt="awesome-gamedev-agent-skills — game-dev skills for AI coding agents.

Stars on GitHub

Momentum

stars in total
8.6k
Star history of gamedev-skills/awesome-gamedev-agent-skills on GitHub, a chart by star-history.com

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills13d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc2w293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react4d252k
@facebook@facebookInstall Skill

Questions about pixijs-rendering.

Which coding agents can use pixijs-rendering?
pixijs-rendering installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.