Skip to main content

PixiJS Update - June

ยท 9 min read
Zyie
PixiJS Admin

Welcome to the June update, covering v8.18.0 and v8.19.0: live HTML-in-Canvas textures, Graphics โ†’ SVG export, official AI agent skills, sprite mask channels, and a milestone we're very proud of.

500,000 weekly downloadsโ€‹

PixiJS crossed 500,000 weekly downloads on npm, and the number has kept climbing since. That's half a million projects, prototypes, games, and experiments pulling PixiJS every single week.

This library exists because of the people who build with it, report bugs, send fixes, and answer questions on Discord. Thank you for building with PixiJS.


Official PixiJS Agent Skillsโ€‹

AI coding agents are now part of many workflows, so we've shipped 25 official PixiJS skills that teach them how to use PixiJS v8 correctly: modern APIs, current best practices, and none of the v7 patterns they tend to hallucinate.

Install them into Claude Code, Cursor, Codex, Copilot, or Windsurf with one command:

npx skills add https://github.com/pixijs/pixijs-skills

As of v8.19.0 the skills also ship inside the npm package itself, available at node_modules/pixi.js/skills/ after install, so your tools can pick them up without a separate step.

Check out the pixijs-skills repository for the full list, and see pixijs.com/llms for everything PixiJS offers AI tooling, from install instructions for 40+ agents to plain-text llms.txt docs.


HTML-in-Canvas texturesโ€‹

The headline feature of v8.19.0: a new opt-in pixi.js/html-source subpath that renders live DOM elements into PixiJS textures. The element stays fully interactive in the browser while it's mirrored to the GPU: inputs stay editable, links stay clickable, and CSS animations keep running.

Here it is running on a real website. Everything you see, including the text being selected and the form being typed into, is a PixiJS texture:

Use HTMLSource for a live element, or ElementImageSource for an immutable snapshot. The element must be a direct child of the Pixi canvas:

import { Application, Sprite } from 'pixi.js';
import { HTMLSource } from 'pixi.js/html-source';

const app = new Application();
await app.init({ resizeTo: window });
document.body.appendChild(app.canvas);

const form = document.createElement('form');
form.innerHTML = '<input value="still editable" />';
app.canvas.appendChild(form); // must be a direct child of the Pixi canvas

const sprite = Sprite.from(new HTMLSource({ resource: form, autoUpdate: true }));
app.stage.addChild(sprite); // live DOM mirrored to the GPU; the form stays interactive

Importing the subpath also registers a lowest-priority Texture.from fallback for generic HTML elements, so apps that don't import it are completely unaffected.

Experimental

This is built on the experimental HTML-in-Canvas browser API, currently available in Chrome behind a flag. If the API isn't enabled in the browser, the texture uploaders throw a clear error. Treat this as a preview of where the web platform is heading.

Thanks to @Zyie for this contribution.


Graphics โ†’ SVG exportโ€‹

v8.18.0 adds graphicsContextToSvg(), a pure function that serializes a Graphics or GraphicsContext into a self-contained SVG string. It supports rects, circles, ellipses, rounded rects, polygons, bezier/quadratic/arc paths, strokes, holes, and linear/radial gradients.

import { Graphics, graphicsContextToSvg } from 'pixi.js';

const g = new Graphics()
.rect(0, 0, 100, 50)
.fill({ color: 0xff0000 })
.circle(150, 25, 25)
.stroke({ color: 0x0000ff, width: 4 });

const svgString = graphicsContextToSvg(g, 2);
await navigator.clipboard.writeText(svgString);

The example below proves the round trip: the right-hand badge is built by feeding the exported SVG string straight back into new Graphics().svg():


Thanks to @GoodBoyDigital for this contribution.


Sprite mask channelsโ€‹

setMask() gains a channel option in v8.18.0, letting you pick which texture channel drives visibility: 'red' (the default, matching previous behavior) or 'alpha'. This matches how design tools like Figma apply PNG masks, so masks exported from design files now work without preprocessing:

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

const photo = new Sprite(await Assets.load('photo.png'));
const maskSprite = new Sprite(await Assets.load('mask-alpha.png'));

photo.setMask({
mask: maskSprite,
channel: 'alpha',
});

Both sides below use the same mask texture: a transparent-backed circle with a red star painted on it. The red channel only sees the star; the alpha channel sees the whole circle:


FillPattern textureSpaceโ€‹

FillPattern gains a textureSpace option in v8.19.0, alongside fixes for several long-standing pattern sizing bugs:

  • 'global' (the new default): the pattern tiles continuously across world space, so adjacent shapes share one seamless grid instead of each remapping the pattern.
  • 'local': one tile is fitted to each shape, and setTransform() lets you subdivide or transform it per shape.
import { Assets, FillPattern, Graphics } from 'pixi.js';

const texture = await Assets.load('pattern.png');

const pattern = new FillPattern({ texture, repetition: 'repeat', textureSpace: 'local' });

const g = new Graphics().rect(0, 0, 200, 100).fill({ fill: pattern });

Behavior Change

Existing pattern fills can render differently after upgrading. setTransform(matrix) now applies the matrix you pass directly (it previously inverted and rescaled it by the texture size), and textureSpace: 'local' radial gradients now scale to the gradient's outer radius. If you hand-compensated for the old setTransform behavior, remove that compensation.


Particles inherit blend modesโ€‹

ParticleContainer now respects the blend mode inherited from its ancestors. Previously, setting a blend mode on a parent (for example stage.blendMode = 'add') was silently ignored by particles; now they resolve the inherited blend mode like every other container. A blendMode set directly on the ParticleContainer works exactly as before.

Both galaxies below are identical ParticleContainers. The only difference is the blendMode set on the right one's parent:


Behavior Change

Particles nested under a parent with a non-default blend mode will now render differently (e.g. additive brightening) where the parent's blend mode was previously ignored.

Thanks to @DmitriyGolub for this contribution.


More additionsโ€‹

  • Renderer preference arrays: autoDetectRenderer and Application.init now accept an array for preference (e.g. ['webgl', 'canvas']), letting you restrict the fallback chain rather than only reorder it (@Zyie).
  • app.domContainerRoot: A read-only getter for the HTMLDivElement that wraps all DOMContainer elements, so you can add CSS classes or styles to the DOM overlay root (@carlos22).
  • Default anchors for generated textures: renderer.generateTexture() accepts a defaultAnchor option, and RenderTexture.create() gains a textureOptions parameter (@ksv90).
  • Transient MSAA attachments (WebGPU): Texture sources gain an opt-in transient flag that lets the WebGPU backend discard MSAA buffers at the end of a render pass instead of writing them back to memory, cutting memory bandwidth on mobile GPUs (@GoodBoyDigital).

Bug fixesโ€‹

Across both releases:

  • Stroke-only Graphics masks now render correctly on the Canvas renderer (@DmitriyGolub).
  • iOS 18.0โ€“18.1 textures: _applyMipRange is skipped for single-mip textures, working around a WebKit bug (@GoodBoyDigital).
  • Stale TextureMatrix when pooled textures reuse the same reference (@GoodBoyDigital).
  • GraphicsPath.transform() now handles all path actions (@Zyie).
  • VideoSource no longer recurses between play and mediaReady before video dimensions are known (@satoren).
  • Tagged text handles literal < characters in parseTaggedText (@glennflanagan).
  • SplitText baseline mismatch fixed when tagStyles is used with lineHeight, and a crash fixed when text begins with whitespace (@Zyie).
  • TilingSprite: tilePosition is no longer divided by resolution on the Canvas renderer, and tileRotation no longer shears the pattern on non-square sprites (@Zyie).
  • BitmapText no longer renders a trailing glyph after a glyph-less word-break character (@Zyie).
  • Canvas renderer rounds the anchor offset when roundPixels is enabled (@Clonex).
  • Custom batchers now receive the renderer's maxBatchableTextures (@SerG-Y).
  • Lost contexts no longer crash shader compilation logging (@Zyie).
  • GCSystem unloads resources before nulling their hash entry (@Zyie).
  • CanvasFilterSystem moved to the filters module (@Zyie).

You can view the full changelogs on GitHub: v8.18.0 and v8.19.0.


New contributorsโ€‹

Welcome to our newest contributors:

Thank you for your contributions!


Get the latest PixiJSโ€‹

Install via npm:

npm install pixi.js@8.19.0

Or use via CDN:

Development builds:

Production builds:

Documentation: https://pixijs.download/v8.19.0/docs/index.html


Wrapping upโ€‹

That covers June's updates. The FillPattern and particle blend mode changes are behavior changes, so check your output after upgrading; the sections above explain how each one moved.

Thanks to everyone who contributed fixes and features to these releases.


Thank you to our sponsorsโ€‹

PixiJS is made possible by our sponsors. A special thanks to our Silver tier and above supporters; you can join them and keep the engine moving.


Happy creating!

The PixiJS Team