Skip to main content
skill CX Codex

pixijs-math — Claude Code skill for PixiJS v8 math

A skill covering coordinates, matrices, shapes and hit testing in PixiJS v8. Useful when a point has to move between coordinate spaces or a container needs a custom hit area.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add pixijs/pixijs-skills --skill pixijs-math -a claude-code
Open on GitHub

What pixijs-math does

The skill documents PixiJS v8 math primitives: Point and ObservablePoint, the affine Matrix with translate, rotate, scale, append, apply, applyInverse and decompose, plus the shape classes Rectangle, Circle, Ellipse, Polygon, RoundedRectangle and Triangle. Every shape carries contains, getBounds and strokeContains.

Rectangle layout helpers get their own section — pad, fit, enlarge, ceil, scale, getBounds — along with containsRect and intersects. Polygon covers isClockwise and containsPolygon, containers cover toGlobal and toLocal. Vector operations and line or segment intersections live in pixi.js/math-extras and need a separate import.

Mirrored matrices are the tricky part. decompose returns a rotation plus a negative scale.x with zero skew when one axis is flipped, and scale.y never comes back negative: a y-axis flip arrives as scale.x = -1 with a half turn. If your code branches on the sign of scale.y, that is worth a look. Point.shared and Matrix.shared are scratch objects, so never keep a reference to them.

What it helps with

  • convert a child container's local point to world space
  • build a matrix from translate, rotate and scale
  • test whether a click lands inside a custom hit area
  • decompose a matrix into position, scale, rotation and skew
  • check whether two rectangles overlap

Example prompts for the agent

  • show me how to get a child container's global position with toGlobal
  • build a Matrix with a 100, 50 translate and a PI/4 rotation
  • make a Rectangle hitArea and test contains for the point 50, 50

How to install pixijs-math

1Skills CLI (Vercel)

npx skills add pixijs/pixijs-skills --skill pixijs-math -a claude-code

2Manually: put the files in place

~/.claude/skills/pixijs-math/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/pixijs-math/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about pixijs-math

How do I install pixijs-math in Claude?

Run npx skills add pixijs/pixijs-skills --skill pixijs-math -a claude-code. The skill file lands at ~/.claude/skills/pixijs-math/SKILL.md.

What does pixijs-math do?

It gives the agent a reference for PixiJS v8 math: points, matrices, shapes, hit tests and coordinate conversion between containers.

Do I need math-extras to work with shapes?

No. Rectangle has containsRect and intersects built in. The pixi.js/math-extras import is for vector operations and line or segment intersections.

Source text

SKILL.md · MIT
---
name: pixijs-math
description: "Use this skill when working with coordinates, vectors, matrices, shapes, hit testing, or layout rectangles in PixiJS v8. Covers Point/ObservablePoint, Matrix (2D affine, decompose (including mirrored/negative-scale matrices), apply, applyInverse), shapes (Rectangle, Circle, Ellipse, Polygon, RoundedRectangle, Triangle), Rectangle layout helpers (pad, fit, enlarge, ceil, scale, getBounds), strokeContains hit tests, Polygon isClockwise/containsPolygon, toGlobal/toLocal, PointData/PointLike/Size types, DEG_TO_RAD, and pixi.js/math-extras vector and intersection helpers. Triggers on: Point, ObservablePoint, Matrix, Rectangle, Circle, Polygon, Triangle, RoundedRectangle, toGlobal, toLocal, hitArea, strokeContains, pad, fit, enlarge, ceil, getBounds, containsRect, intersects, isClockwise, math-extras, lineIntersection, segmentIntersection, DEG_TO_RAD, PointData, decompose, mirror, flip, negative scale, setFromMatrix."
license: MIT
---

PixiJS exposes lightweight math primitives (Point, Matrix, shape classes) used throughout the library for transforms, hit testing, and coordinate conversion. Import `pixi.js/math-extras` to add vector operations (add, dot, magnitude, reflect) and Rectangle intersection/union helpers.

## Quick Start

```ts
const parent = new Container();
parent.position.set(100, 100);
parent.scale.set(2);
app.stage.addChild(parent);

const child = new Container();
child.position.set(50, 50);
parent.addChild(child);

const globalPt = child.toGlobal(new Point(0, 0));

const m = new Matrix()
  .translate(100, 50)
  .rotate(Math.PI / 4)
  .scale(2, 2);
const world = m.apply(new Point(10, 20));

const hitArea = new Rectangle(0, 0, 200, 100);
console.log(hitArea.contains(50, 50));
```

**Related skills:** `pixijs-scene-container` (transform properties), `pixijs-events` (hitArea usage), `pixijs-scene-core-concepts` (culling with Rectangle).

## Core Patterns

### Point and ObservablePoint

Point is a simple {x, y} value type. ObservablePoint fires a callback when x or y changes; it is used internally by Container's position, scale, pivot, origin, and skew.

```ts
import { Point } from "pixi.js";

const p = new Point(10, 20);
p.set(30, 40); // set both
p.set(50); // x=50, y=50

const clone = p.clone();
console.log(p.equals(clone)); // true

p.copyFrom({ x: 1, y: 2 }); // accepts any PointData

// Point.shared: temporary point, reset to (0,0) on each access
const temp = Point.shared;
temp.set(100, 200);
// do not store a reference to Point.shared
```

Container properties like `position`, `scale`, `pivot`, `origin`, and `skew` are ObservablePoints. Setting `.x` or `.y` on them triggers transform recalculation automatically.

```ts
import { Container } from "pixi.js";

const obj = new Container();
obj.position.set(100, 200); // triggers observer -> marks transform dirty
obj.position.x = 150; // also triggers observer
```

### Matrix (2D affine transform)

Matrix represents a 3x3 affine transform: `| a c tx | b d ty | 0 0 1 |`. It supports translate, scale, rotate, append, prepend, invert, and decompose.

```ts
import { Matrix, Point } from "pixi.js";

// Build a transform
const m = new Matrix()
  .translate(100, 50)
  .rotate(Math.PI / 4)
  .scale(2, 2);

// Transform a point (local -> parent space)
const local = new Point(10, 20);
const world = m.apply(local);

// Inverse transform (parent -> local space)
const backToLocal = m.applyInverse(world);

// Combine matrices
const a = new Matrix().translate(50, 0);
const b = new Matrix().rotate(Math.PI / 2);
a.append(b); // a = a * b

// Decompose into position/scale/rotation/skew
const transform = {
  position: new Point(),
  scale: new Point(),
  pivot: new Point(),
  skew: new Point(),
  rotation: 0,
};
m.decompose(transform);
console.log(transform.rotation); // ~0.785 (PI/4)

// A mirrored matrix (one axis flipped, axes still perpendicular) decomposes into
// rotation + negative scale.x with zero skew. scale.y is never negative: a y-axis
// flip comes back as scale.x = -1 plus a half turn. Either way the parts rebuild the matrix.
const mirrored = new Matrix().rotate(Math.PI / 6).scale(-1, 1);
mirrored.decompose(transform);
transform.scale.x; // -1
transform.skew.x; // 0

// Shared temporary matrix (reset on each access)
const temp = Matrix.shared;
// IDENTITY is read-only reference
const isDefault = m.equals(Matrix.IDENTITY);
```

### Coordinate transforms via Container

Containers provide `toGlobal`, `toLocal`, and `getGlobalPosition` for coordinate conversion.

```ts
import { Container, Point } from "pixi.js";

const parent = new Container();
parent.position.set(100, 100);
parent.scale.set(2);

const child = new Container();
child.position.set(50, 50);
parent.addChild(child);

// Local point in child's space -> global (world) space
const globalPt = child.toGlobal(new Point(0, 0));
// globalPt = { x: 200, y: 200 } (100 + 50*2, 100 + 50*2)

// Global point -> child's local space
const localPt = child.toLocal(new Point(200, 200));
// localPt = { x: 0, y: 0 }

// Convert between two containers
const other = new Container();
other.position.set(300, 300);
const ptInOther = child.toLocal(new Point(10, 10), other);
```

### Shapes and hit testing

Rectangle, Circle, Ellipse, Polygon, RoundedRectangle, and Triangle all implement `contains(x, y)` for point-in-shape tests, plus `getBounds(out?)` and `strokeContains(x, y, width, alignment?)` (`Triangle` ignores `alignment` and always tests a centered stroke). They can be used as `hitArea` on containers for custom interaction regions.

```ts
import { Rectangle, Circle, Ellipse, Polygon, Container } from "pixi.js";

const rect = new Rectangle(0, 0, 200, 100);
rect.contains(50, 50); // true
rect.contains(300, 50); // false
rect.left; // 0
rect.right; // 200
rect.top; // 0
rect.bottom; // 100
rect.isEmpty(); // false (Rectangle.EMPTY returns a fresh empty rect)

// Native Rectangle-to-Rectangle methods (no math-extras needed)
const other = new Rectangle(50, 50, 100, 100);
rect.containsRect(other); // true if `other` is fully inside `rect`
rect.containsRect(new Rectangle(100, 0, 100, 100)); // true: a flush right edge counts
rect.containsRect(rect.clone()); // true: identical rects contain each other
rect.intersects(other); // boolean: do they overlap at all?
rect.intersects(other, matrix); // overlap after transforming `other`

// Stroke hit testing (alignment: 1 = inner, 0.5 = centered, 0 = outer)
rect.strokeContains(0, 50, 4); // true if (0,50) lies on a 4px centered stroke
const circle = new Circle(100, 100, 50);
circle.strokeContains(150, 100, 4, 1); // inner-aligned stroke check
// A stroke that reaches past the shape's centre covers the whole interior; there is no hole
new Ellipse(0, 0, 10, 10).strokeContains(0, 0, 40, 1); // true

// getBounds works on every shape (returns a Rectangle, accepts an out param)
const bounds = circle.getBounds();
const reused = new Rectangle();
new Polygon([0, 0, 100, 0, 50, 100]).getBounds(reused);

// Use as hit area for interaction
const button = new Container();
button.hitArea = new Rectangle(0, 0, 200, 50);
button.eventMode = "static";
button.on("pointerdown", () => {
  /* clicked */
});
```

Do not confuse native `Rectangle.intersects(other)` (returns `boolean`) with math-extras `intersection(other)` (returns a `Rectangle` describing the overlap area).

### Rectangle layout helpers

Rectangle ships with mutating helpers used heavily in UI/layout, bounds aggregation, and pixel snapping. All return `this` for chaining.

```ts
import { Rectangle } from "pixi.js";

const r = new Rectangle(10, 10, 100, 50);

r.pad(5); // grow on all sides: x=5, y=5, w=110, h=60
r.pad(10, 4); // separate horizontal/vertical padding
r.scale(2); // multiply x, y, width, height by 2

// fit shrinks `this` to lie inside another rect (clipping)
const viewport = new Rectangle(0, 0, 200, 200);
new Rectangle(150, 150, 200, 200).fit(viewport); // -> 150, 150, 50, 50

// enlarge expands `this` to include another rect (bounds aggregation)
const total = new Rectangle();
items.forEach((item) =>
  total.enlarge(new Rectangle().copyFromBounds(item.getBounds())),
);

// ceil snaps to a pixel grid (resolution: 1 = whole pixels, 2 = half pixels)
new Rectangle(10.2, 10.6, 100.8, 100.4).ceil();

// copy a Container/Mesh bounds object straight into a Rectangle
new Rectangle().copyFromBounds(container.getBounds());
```

### Polygon

Polygon accepts four constructor formats: a flat number array, an array of point-like objects, or either passed as spread arguments.

```ts
import { Polygon, Point } from "pixi.js";

new Polygon([0, 0, 100, 0, 50, 100]); // flat numbers
new Polygon([new Point(0, 0), new Point(100, 0), new Point(50, 100)]); // PointData[]
new Polygon(0, 0, 100, 0, 50, 100); // spread numbers
new Polygon(new Point(0, 0), new Point(100, 0), new Point(50, 100)); // spread points

const poly = new Polygon([0, 0, 100, 0, 100, 100, 0, 100]);
poly.points; // [0, 0, 100, 0, 100, 100, 0, 100] (mutable flat array)
poly.closePath; // true by default; false produces an open path
poly.startX; // 0  - first vertex
poly.lastX; // 0  - last vertex (lastY for y)
poly.isClockwise(); // shoelace winding test (useful for SVG hole detection)

// Polygon-in-polygon containment for hole detection
const outer = new Polygon([0, 0, 100, 0, 100, 100, 0, 100]);
const hole = new Polygon([25, 25, 75, 25, 75, 75, 25, 75]);
outer.containsPolygon(hole); // true

// Stroke hit test follows the drawn stroke: alignment 1 = inside, 0.5 = centered,
// 0 = outside, independent of winding order
outer.strokeContains(-5, 50, 20); // true (centered stroke extends 10px outward)
outer.strokeContains(-5, 50, 20, 1); // false (inner stroke stays inside)
outer.strokeContains(5, 50, 20, 0); // false (outer stroke stays outside)
```

### Constants

```ts
import { DEG_TO_RAD, RAD_TO_DEG, PI_2 } from "pixi.js";

const angle = 45 * DEG_TO_RAD; // 0.785...
const degrees = angle * RAD_TO_DEG; // 45
const fullCircle = PI_2; // Math.PI * 2
```

### Types

- `PointData` - minimal `{x, y}` interface accepted by most APIs. Use it when typing parameters that only need to read coordinates.
- `PointLike` - extends `PointData` with `set()`, `copyFrom()`, `copyTo()`, `equals()`. Implemented by both `Point` and `ObservablePoint`.
- `Size` - `{ width, height }` interface used by renderer/canvas APIs.
- `SHAPE_PRIMITIVE` - string literal union: `'rectangle' | 'circle' | 'ellipse' | 'polygon' | 'roundedRectangle' | 'triangle'`. Every shape exposes `type` so you can branch without `instanceof`.

```ts
import type { PointData } from "pixi.js";

function distance(a: PointData, b: PointData): number {
  const dx = a.x - b.x;
  const dy = a.y - b.y;
  return Math.sqrt(dx * dx + dy * dy);
}
```

### math-extras (side-effect import)

`import 'pixi.js/math-extras'` adds methods to Point, ObservablePoint, and Rectangle via prototype extension. Not included in the default bundle.

```ts
import "pixi.js/math-extras";
import { Point } from "pixi.js";
```

#### Point / ObservablePoint vector methods

All methods accept an optional `out` parameter to avoid allocations. Without `out`, a new Point is returned.

```ts
const a = new Point(3, 4);
const b = new Point(1, 2);

// Arithmetic
const sum = a.add(b); // Point(4, 6)
const diff = a.subtract(b); // Point(2, 2)
const prod = a.multiply(b); // Point(3, 8) - component-wise
const scaled = a.multiplyScalar(2); // Point(6, 8)

// Dot and cross product
const dot = a.dot(b); // 11
const cross = a.cross(b); // 2 (z-component of 3D cross)

// Length
const len = a.magnitude(); // 5
const lenSq = a.magnitudeSquared(); // 25 (faster for comparisons)

// Normalize to unit vector
const unit = a.normalize(); // Point(0.6, 0.8)

// Projection and reflection
const proj = a.project(b); // project a onto b
const refl = a.reflect(new Point(0, 1)); // reflect across normal

// Rotation
const rotated = a.rotate(Math.PI / 2); // rotate 90 degrees

// Reuse existing point to avoid allocation
const out = new Point();
a.add(b, out); // result written to out
```

#### Rectangle extended methods

`containsRect` and `intersects` are native `Rectangle` methods (see above). math-extras adds `equals`, `intersection` (returns the overlap rect), and `union`:

```ts
import "pixi.js/math-extras";
import { Rectangle } from "pixi.js";

const r1 = new Rectangle(0, 0, 100, 100);
const r2 = new Rectangle(50, 50, 100, 100);

r1.equals(r2); // false

const overlap = r1.intersection(r2); // Rectangle(50, 50, 50, 50)
const envelope = r1.union(r2); // Rectangle(0, 0, 150, 150)

// Optional out parameter
const out = new Rectangle();
r1.intersection(r2, out);
```

#### Geometry utility functions

These functions are exported from `pixi.js/math-extras`, not the main `pixi.js` entry.

```ts
import {
  floatEqual,
  lineIntersection,
  segmentIntersection,
} from "pixi.js/math-extras";
import { Point } from "pixi.js";

// Epsilon-based float comparison (default epsilon: Number.EPSILON)
floatEqual(0.1 + 0.2, 0.3, 1e-10); // true with reasonable epsilon
floatEqual(1.0, 1.001, 0.01); // true (custom epsilon)

// Unbounded line intersection (returns {x: NaN, y: NaN} if parallel)
const hit = lineIntersection(
  new Point(0, 0),
  new Point(10, 10), // line A
  new Point(10, 0),
  new Point(0, 10), // line B
); // Point(5, 5)
if (isNaN(hit.x)) {
  /* lines are parallel */
}

// Bounded segment intersection (returns {x: NaN, y: NaN} if segments don't cross)
const segHit = segmentIntersection(
  new Point(0, 0),
  new Point(10, 10),
  new Point(10, 0),
  new Point(0, 10),
); // Point(5, 5)
if (isNaN(segHit.x)) {
  /* segments don't intersect */
}
```

## Common Mistakes

### HIGH: Importing from @pixi/math

Wrong:

```ts
import { Point } from "@pixi/math";
```

Correct:

```ts
import { Point } from "pixi.js";
```

v8 uses a single `pixi.js` package. All sub-packages like `@pixi/math`, `@pixi/core`, etc. were removed.


### MEDIUM: Mutating ObservablePoint without triggering observer

Wrong:

```ts
// Replacing the reference loses observation
let pos = container.position;
pos = new Point(100, 200); // container.position unchanged
```

Correct:

```ts
// Mutate in place to trigger the observer
container.position.set(100, 200);
// or
container.position.x = 100;
container.position.y = 200;
// or copy from another point
container.position.copyFrom(new Point(100, 200));
```

Container's position, scale, pivot, origin, and skew are ObservablePoints. Setting `.x` or `.y` on them triggers the container's transform update. Reassigning the variable reference does not modify the container. Always mutate the existing ObservablePoint via `.set()`, `.copyFrom()`, or direct property assignment on the original object.


### MEDIUM: Not importing math-extras for extended methods

Wrong:

```ts
import { Point } from "pixi.js";
const p = new Point(1, 2);
p.add(new Point(3, 4)); // TypeError: p.add is not a function
```

Correct:

```ts
import "pixi.js/math-extras";
import { Point } from "pixi.js";
const p = new Point(1, 2);
const sum = p.add(new Point(3, 4)); // works
```

Extended math utilities (add, subtract, multiply, magnitude, normalize, dot, cross, etc. on Point; intersection methods on shapes) require an explicit `import 'pixi.js/math-extras'`. These are not included in the default bundle.


### MEDIUM: Storing references to shared/temporary objects

Wrong:

```ts
const myPoint = Point.shared;
myPoint.set(100, 200);
// ... later ...
console.log(myPoint.x); // 0 (reset on next access)
```

Correct:

```ts
const myPoint = new Point();
myPoint.copyFrom(Point.shared.set(100, 200));
// or just
const myPoint = new Point(100, 200);
```

`Point.shared` and `Matrix.shared` are reset to zero/identity every time they are accessed. They exist for one-off calculations within a single expression. Never store a reference to them.


## API Reference

- [Point](https://pixijs.download/release/docs/maths.Point.html.md)
- [ObservablePoint](https://pixijs.download/release/docs/maths.ObservablePoint.html.md)
- [Matrix](https://pixijs.download/release/docs/maths.Matrix.html.md)
- [Rectangle](https://pixijs.download/release/docs/maths.Rectangle.html.md)
- [Circle](https://pixijs.download/release/docs/maths.Circle.html.md)
- [Ellipse](https://pixijs.download/release/docs/maths.Ellipse.html.md)
- [Polygon](https://pixijs.download/release/docs/maths.Polygon.html.md)
- [RoundedRectangle](https://pixijs.download/release/docs/maths.RoundedRectangle.html.md)
- [Triangle](https://pixijs.download/release/docs/maths.Triangle.html.md)
- [DEG_TO_RAD](https://pixijs.download/release/docs/maths.DEG_TO_RAD.html.md)

© pixijs · MIT · pixijs/pixijs-skills

Similar skills

Everything for Game dev and 3D: Unity, Unreal, Godot, Blender →

From the same set26

pixijs-filters Guides PixiJS v8 visual effects through the filter pipeline. Use it for blur, colour adjustments, displacement, or custom shader effects. CX Codex +1 5,800installs pixijs-ticker A skill for per-frame logic in PixiJS v8: ticker callbacks, time units, priority ordering and frame rate capping. CX Codex +1 5,800installs pixijs-scene-text Teaches coding agents to render text in PixiJS v8 properly. Helps pick between bitmap, HTML, and canvas rendering without stalling frame updates. CX Codex +1 5,800installs pixijs-scene-core-concepts Explains how the PixiJS v8 scene graph fits together: containers, leaves, transforms and render order. Useful when your agent writes PixiJS code. CX Codex +1 5,900installs pixijs-scene-mesh Guides custom 2D geometry rendering in PixiJS v8 via Mesh, MeshPlane, MeshRope, and PerspectiveMesh. Useful for per-frame vertex animations, rope trails, and perspective distortion. CX Codex +1 5,700installs pixijs-scene-dom-container Teaches the agent how to overlay HTML elements on a PixiJS v8 canvas with DOMContainer and DOMPipe. Useful for native inputs, iframes or video that must follow a display object. CX Codex +1 5,700installs pixijs-blend-modes A skill that tells your agent how to composite PixiJS v8 display objects with blendMode: standard modes, advanced ones, and batch-friendly ordering. CX Codex +1 5,700installs pixijs-color PixiJS v8 skill for the Color class. It guides color conversions, tint calculations, and buffer reuse to prevent GC spikes in animation loops. CX Codex +1 5,700installs

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor