| 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, 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. |
| 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
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.
import { Point } from "pixi.js";
const p = new Point(10, 20);
p.set(30, 40);
p.set(50);
const clone = p.clone();
console.log(p.equals(clone));
p.copyFrom({ x: 1, y: 2 });
const temp = Point.shared;
temp.set(100, 200);
Container properties like position, scale, pivot, origin, and skew are ObservablePoints. Setting .x or .y on them triggers transform recalculation automatically.
import { Container } from "pixi.js";
const obj = new Container();
obj.position.set(100, 200);
obj.position.x = 150;
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.
import { Matrix, Point } from "pixi.js";
const m = new Matrix()
.translate(100, 50)
.rotate(Math.PI / 4)
.scale(2, 2);
const local = new Point(10, 20);
const world = m.apply(local);
const backToLocal = m.applyInverse(world);
const a = new Matrix().translate(50, 0);
const b = new Matrix().rotate(Math.PI / 2);
a.append(b);
const transform = {
position: new Point(),
scale: new Point(),
pivot: new Point(),
skew: new Point(),
rotation: 0,
};
m.decompose(transform);
console.log(transform.rotation);
const temp = Matrix.shared;
const isDefault = m.equals(Matrix.IDENTITY);
Coordinate transforms via Container
Containers provide toGlobal, toLocal, and getGlobalPosition for coordinate conversion.
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);
const globalPt = child.toGlobal(new Point(0, 0));
const localPt = child.toLocal(new Point(200, 200));
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?). They can be used as hitArea on containers for custom interaction regions.
import { Rectangle, Circle, Polygon, Container } from "pixi.js";
const rect = new Rectangle(0, 0, 200, 100);
rect.contains(50, 50);
rect.contains(300, 50);
rect.left;
rect.right;
rect.top;
rect.bottom;
rect.isEmpty();
const other = new Rectangle(50, 50, 100, 100);
rect.containsRect(other);
rect.intersects(other);
rect.intersects(other, matrix);
rect.strokeContains(0, 50, 4);
const circle = new Circle(100, 100, 50);
circle.strokeContains(150, 100, 4, 1);
const bounds = circle.getBounds();
const reused = new Rectangle();
new Polygon([0, 0, 100, 0, 50, 100]).getBounds(reused);
const button = new Container();
button.hitArea = new Rectangle(0, 0, 200, 50);
button.eventMode = "static";
button.on("pointerdown", () => {
});
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.
import { Rectangle } from "pixi.js";
const r = new Rectangle(10, 10, 100, 50);
r.pad(5);
r.pad(10, 4);
r.scale(2);
const viewport = new Rectangle(0, 0, 200, 200);
new Rectangle(150, 150, 200, 200).fit(viewport);
const total = new Rectangle();
items.forEach((item) => total.enlarge(new Rectangle().copyFromBounds(item.getBounds())));
new Rectangle(10.2, 10.6, 100.8, 100.4).ceil();
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.
import { Polygon, Point } from "pixi.js";
new Polygon([0, 0, 100, 0, 50, 100]);
new Polygon([new Point(0, 0), new Point(100, 0), new Point(50, 100)]);
new Polygon(0, 0, 100, 0, 50, 100);
new Polygon(new Point(0, 0), new Point(100, 0), new Point(50, 100));
const poly = new Polygon([0, 0, 100, 0, 100, 100, 0, 100]);
poly.points;
poly.closePath;
poly.startX;
poly.lastX;
poly.isClockwise();
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);
Constants
import { DEG_TO_RAD, RAD_TO_DEG, PI_2 } from "pixi.js";
const angle = 45 * DEG_TO_RAD;
const degrees = angle * RAD_TO_DEG;
const fullCircle = 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.
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.
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.
const a = new Point(3, 4);
const b = new Point(1, 2);
const sum = a.add(b);
const diff = a.subtract(b);
const prod = a.multiply(b);
const scaled = a.multiplyScalar(2);
const dot = a.dot(b);
const cross = a.cross(b);
const len = a.magnitude();
const lenSq = a.magnitudeSquared();
const unit = a.normalize();
const proj = a.project(b);
const refl = a.reflect(new Point(0, 1));
const rotated = a.rotate(Math.PI / 2);
const out = new Point();
a.add(b, out);
Rectangle extended methods
containsRect and intersects are native Rectangle methods (see above). math-extras adds equals, intersection (returns the overlap rect), and union:
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);
const overlap = r1.intersection(r2);
const envelope = r1.union(r2);
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.
import { floatEqual, lineIntersection, segmentIntersection } from "pixi.js/math-extras";
import { Point } from "pixi.js";
floatEqual(0.1 + 0.2, 0.3, 1e-10);
floatEqual(1.0, 1.001, 0.01);
const hit = lineIntersection(
new Point(0, 0),
new Point(10, 10),
new Point(10, 0),
new Point(0, 10),
);
if (isNaN(hit.x)) {
}
const segHit = segmentIntersection(
new Point(0, 0),
new Point(10, 10),
new Point(10, 0),
new Point(0, 10),
);
if (isNaN(segHit.x)) {
}
Common Mistakes
HIGH: Importing from @pixi/math
Wrong:
import { Point } from "@pixi/math";
Correct:
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:
let pos = container.position;
pos = new Point(100, 200);
Correct:
container.position.set(100, 200);
container.position.x = 100;
container.position.y = 200;
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:
import { Point } from "pixi.js";
const p = new Point(1, 2);
p.add(new Point(3, 4));
Correct:
import "pixi.js/math-extras";
import { Point } from "pixi.js";
const p = new Point(1, 2);
const sum = p.add(new Point(3, 4));
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:
const myPoint = Point.shared;
myPoint.set(100, 200);
console.log(myPoint.x);
Correct:
const myPoint = new Point();
myPoint.copyFrom(Point.shared.set(100, 200));
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