Creative Coding

PixiJS 8.21 Changes What Matrix.decompose() Hands Back, and Your Flip Detection May Care

Rendering is identical. The numbers you read out of a mirrored matrix are not — and that is exactly the kind of change that breaks a tween six months later.

The most dangerous kind of library change is the one that doesn’t break anything visibly. PixiJS v8.21.0, released September 17, 2026, contains one, and the release notes flag it properly as a behaviour change.

What changed

Matrix.decompose() and setFromMatrix() — plus reparentChild(), reparentChildAt() and replaceChild(), which use them — now decompose a mirrored matrix into a real rotation, zero skew, and a negative scale.x. Previously they returned rotation = 0 with a nonzero skew.

From the release notes:

import { Container, Matrix } from 'pixi.js';

const container = new Container();
container.setFromMatrix(new Matrix().rotate(Math.PI / 6).scale(-1, 1));

// before: rotation 0, skew { x: ~0.524, y: ~2.618 }, scale { x: 1, y: 1 }
// after:  rotation ~-0.524, skew { x: 0, y: 0 }, scale { x: -1, y: 1 }

The flip always lands on scale.x. So a scale(1, -1) matrix now reports scale (-1, 1) with rotation = -π.

Rendering is identical. Only the values you read back change.

Why this bites

Because a mirrored sprite is an extremely common thing, and reading decomposed values is an extremely common way to reason about one.

Three patterns that break quietly:

  • Flip detection. Code that asks “is this sprite mirrored?” by checking skew will now always say no. Code checking scale.x < 0 now works and previously didn’t. If you wrote the former as a workaround, it silently stops working.
  • Tweens starting from decomposed values. Grab the current rotation, animate to a target. Previously you started at 0 and had a skew you were ignoring; now you start at -0.524. The animation still runs — to a different place.
  • Serialising transforms. Anything writing rotation/scale/skew to a save file, undo stack or network message now produces different numbers for the same visual state. Old data deserialises to a matrix that renders the same, so you may not notice until you diff two states and they disagree.

The release notes say it plainly: “Re-check any flip detection or tweens that start from decomposed values.” Take that literally.

Is the new behaviour right?

Yes, and that’s why it shipped. Reporting a mirror as rotation = 0 plus a weird skew pair is mathematically defensible but practically useless — skew of ~2.618 tells a human nothing, while scale.x = -1 says “this is flipped.” The new decomposition is the one you’d want if you were choosing from scratch.

It is still a change to a public API’s output, in a minor version, in a library used heavily for interactive and generative web work. Worth reading before you bump.

The other notable addition

TypeScript 6 and 7 support. PixiJS types now compile on TS 6 and 7 as well as 5. TypeScript 5 needs no changes, and PixiJS continues to pull in @webgpu/types for you.

That’s unglamorous and genuinely useful — a rendering library that won’t compile on your toolchain is a rendering library you can’t use, and the TS 6/7 transition has stranded plenty of dependencies.

Getting it

npm install pixi.js@8.21.0

Full comparison against 8.20.1 is on GitHub, and the versioned docs are published per release. For anyone maintaining a PixiJS project, this is a read-the-notes upgrade rather than a blind one — which is a compliment to the notes as much as a warning about the change.