Functions
backward
function backward(yaw: number, pitch = 0): { x: number; y: number; z: number }
From @phaser-ae/kernel
Unit BACKWARD direction — the opposite of forward. Reverse, recoil,
a chase camera's eye offset (target − forward × distance is the same thing
as target + backward × distance).
composeChain
function composeChain( localX: number, localY: number, localZ: number, localYaw: number, localPitch: number, localRoll: number, parent: ParentLike | null | undefined, ): { x: number; y: number; z: number; yaw: number; pitch: number; roll: number; scale: number }
From @phaser-ae/kernel
Compose a child's LOCAL pose through its parent chain into world space.
Returns world position, world euler (engine order) and the accumulated
uniform scale. Guards against cycles (depth cap). Mesh parents contribute
position + rotation only — a crate's SIZE never inflates its rider;
a parent's scale is the deliberate scaling knob.
down
function down(yaw: number, pitch = 0): { x: number; y: number; z: number }
From @phaser-ae/kernel
Unit DOWN direction for a yaw/pitch — the opposite of up.
dragToLook
function dragToLook(d: DragDelta, sensitivity: number, invertY = false): TurnDelta
From @phaser-ae/kernel
LOOK — first person. Direct: the view turns the way your hand goes.
Drag RIGHT → the view turns right. Drag DOWN → the view looks down.
invertY is the one preference a player genuinely expects to find in a menu; nothing else here is a
matter of taste.
dragToOrbit
function dragToOrbit(d: DragDelta, sensitivity: number, pitchScale = 0.8): TurnDelta
From @phaser-ae/kernel
ORBIT — a camera circling a subject. Grab-and-drag on the WORLD: the scene follows your hand, so the camera travels the opposite way.
Drag RIGHT → the scene appears to rotate right (the eye orbits to the camera's left). Drag DOWN → the eye rises, so you look down on the subject — the same as pulling its top toward you.
sensitivity is radians per pixel. pitchScale trims the vertical rate against the horizontal, because a
screen is wider than it is tall and an equal rate feels twice as fast going up.
dragToSpin
function dragToSpin(d: DragDelta, sensitivity: number, pitchScale = 0.8): TurnDelta
From @phaser-ae/kernel
SPIN — a model on a turntable. Grab-and-drag on the OBJECT: it follows your hand.
Drag RIGHT → the object turns right (its left face swings toward you). Drag DOWN → the object tips its top toward you.
The visible result matches ORBIT, which is the whole trap: the same picture, reached by turning the object rather than the camera. Use this when the subject is one thing and the camera should stay put — an inspector, a character select, a product view — and ORBIT when the camera is exploring a scene.
eulerFromBasis
function eulerFromBasis( fx: number, fy: number, fz: number, ux: number, uy: number, uz: number, ): [number, number, number]
From @phaser-ae/kernel
The engine euler (yaw, pitch, roll) that points an object's local +z along forward and its local +y
along up — the pose a thing riding a curve wants, where forward is the tangent and up is the
frame's normal. up need not be perpendicular: the component along forward is removed first.
It is the way to orient anything on a path — a cart on a rail, a plane on a route, a camera on a dolly — because a path frame gives vectors and every object wears eulers.
A frame that INVERTS (a loop) is carried correctly: the normal turns over and the roll follows it, which is what makes a coaster train hang upside down rather than flip its yaw round.
eulerToQuat
function eulerToQuat(yaw: number, pitch: number, roll: number): Quat
From @phaser-ae/kernel
Quaternion for the engine euler order: q = qY(yaw) · qX(pitch) · qZ(roll). YAW HANDEDNESS: yaw is CLOCKWISE-from-above — increasing yaw turns to the RIGHT (the mainstream / three.js / FPS sense). That is baked in here by building qY from −yaw (sy is negated); the WGSL rotate(), forward(), lookAtEuler(), physics and picking all share this one sign.
forward
function forward(yaw: number, pitch = 0): { x: number; y: number; z: number }
From @phaser-ae/kernel
Unit forward direction for a yaw/pitch (engine rotate order, roll 0) — the
inverse of lookAtEuler: forward(yaw, pitch) = (−cos p·sin y, −sin p, cos p·cos y). +Z is forward at yaw 0; INCREASING yaw turns to the RIGHT
(mainstream / three.js / FPS). Use it for first-person movement and camera
aim.
frustumPlanes
function frustumPlanes(m: Mat4): Float32Array
From @phaser-ae/kernel
Extract the six frustum planes of a column-major viewProj (WebGPU depth 0..1; works for PERSPECTIVE and ORTHO alike): 24 floats, [a, b, c, d] × (left, right, bottom, top, near, far), xyz normalised. A point is inside a plane when a·x + b·y + c·z + d ≥ 0. Gribb–Hartmann, near plane from the z ≥ 0 clip (not GL's z ≥ -w).
left
function left(yaw: number): { x: number; y: number; z: number }
From @phaser-ae/kernel
Unit LEFT direction, HORIZONTAL — the opposite of right.
left(0) = (1, 0, 0).
lookAtEuler
function lookAtEuler( fromX: number, fromY: number, fromZ: number, toX: number, toY: number, toZ: number, ): { yaw: number; pitch: number }
From @phaser-ae/kernel
Yaw/pitch that aim local +Z at a target (engine rotate order, roll 0): forward(yaw, pitch) = (−cos p · sin y, −sin p, cos p · cos y).
mat4Inverse
function mat4Inverse(m: Mat4): Mat4
From @phaser-ae/kernel
General 4×4 inverse (column-major, cofactor expansion). Returns identity for a singular input rather than NaNs — callers unproject with it and a degenerate frame should read as "no transform", never as poison.
mat4LookAt
function mat4LookAt(eye: V3, target: V3, up: V3 = vec3(0, 1, 0)): Mat4
From @phaser-ae/kernel
A view matrix looking from eye at target (y-up world).
mat4Mul
function mat4Mul(a: Mat4, b: Mat4): Mat4
From @phaser-ae/kernel
m = a × b (column-major).
mat4Ortho
function mat4Ortho(l: number, r: number, b: number, t: number, near: number, far: number): Mat4
From @phaser-ae/kernel
Orthographic projection (WebGPU depth 0..1) — shadow-map cameras.
mat4Perspective
function mat4Perspective(fovYDeg: number, aspect: number, near: number, far: number): Mat4
From @phaser-ae/kernel
Perspective projection (fovY degrees, WebGPU depth range 0..1).
mat4Project
function mat4Project(m: Mat4, p: V3): V3
From @phaser-ae/kernel
Transform a point (w=1) and perspective-divide — for tests + picking.
meshCullRadius
function meshCullRadius(verts: ArrayLike<number>, stride = 8): number
From @phaser-ae/kernel
A mesh's CULL RADIUS at scale 1: the sphere about its origin that holds every vertex. verts are interleaved,
position first, stride floats a vertex (8 for a registered mesh: pos, normal, uv). Times an object's largest
|scale| it bounds the object however it is turned, so a cull stays one sphere test whatever size the mesh was
authored at. A mesh drawn off to one side of its origin gets a sphere that reaches it, not a tight one.
Never less than UNIT_MESH_RADIUS. Surfaces that move vertices on the GPU (a sway, a bend, a wobble) reach
past the vertices registered, and a unit mesh's corner is the slack they have always had, so nothing is culled
tighter than a one-unit cube would be.
quatMul
function quatMul(a: Quat, b: Quat): Quat
From @phaser-ae/kernel
Returns the quaternion product a · b: apply b first, then a.
quatRotate
function quatRotate(q: Quat, x: number, y: number, z: number): [number, number, number]
From @phaser-ae/kernel
Rotate a point by a quaternion (q v q*).
quatToEuler
function quatToEuler(q: Quat): [number, number, number]
From @phaser-ae/kernel
Quaternion → the engine's yaw/pitch/roll (Ry·Rx·Rz): the inverse of eulerToQuat. At a pitch pole (±π/2) roll is 0 and yaw takes the whole turn.
rayFromNdc
function rayFromNdc(invVp: Mat4, nx: number, ny: number): { origin: V3; dir: V3 }
From @phaser-ae/kernel
A world-space picking ray from an NDC point (nx, ny in -1..1, y UP) —
unproject near and far and take the difference. invVp is the inverse
of the camera's viewProj (mat4Inverse). Returns unit dir.
rayObb
function rayObb( origin: V3, dir: V3, center: V3, half: V3, yaw: number, pitch: number, roll: number, ): number | null
From @phaser-ae/kernel
Ray vs an ORIENTED box (the engine euler order: roll about z, pitch about x, yaw about y — the mesh shader's rotate()): nearest positive hit distance or null. Exact for boxes and a tight proxy for everything else — bounding SPHERES are useless for flat meshes (a ground disc's sphere swallows every click in the scene).
rayObbLocal
function rayObbLocal( origin: V3, dir: V3, pos: V3, size: V3, yaw: number, pitch: number, roll: number, bounds?: { cx: number; cy: number; cz: number; hx: number; hy: number; hz: number }, ): number | null
From @phaser-ae/kernel
Ray vs an oriented box derived from a mesh's LOCAL geometry AABB. pos +
size (w,h,d) + euler place a UNIT-fit geometry; bounds is that
geometry's local AABB (centre + half-extents, unit space). The box is the
AABB scaled by size, its centre offset rotated into world (mesh euler
order: roll·z → pitch·x → yaw·y). With no bounds, falls back to the
symmetric size/2 cube (only exact for box). Nearest positive t or null.
This is what makes picking tight for off-centre / thin / multi-part model geometry, whose symmetric w/h/d cube overshoots and swallows clicks behind it.
rayPlaneY
function rayPlaneY(origin: V3, dir: V3, h: number): V3 | null
From @phaser-ae/kernel
Ray vs the horizontal plane y = h: hit point, or null (parallel/behind).
raySphere
function raySphere(origin: V3, dir: V3, c: V3, r: number): number | null
From @phaser-ae/kernel
Ray vs sphere: nearest positive hit distance, or null.
right
function right(yaw: number): { x: number; y: number; z: number }
From @phaser-ae/kernel
Unit RIGHT direction, HORIZONTAL — the direction increasing yaw turns
toward. Strafing, steering, lane offsets and anything mounted on the right
of a body. Pitch is deliberately ignored: a player looking at the sky still
strafes along the ground. right(0) = (−1, 0, 0).
slerpEuler
function slerpEuler( yaw0: number, pitch0: number, roll0: number, yaw1: number, pitch1: number, roll1: number, a: number, out: number[] | Float32Array | Float64Array, ): void
From @phaser-ae/kernel
Blend one orientation toward another and write the result back as yaw/pitch/roll.
Euler angles cannot be blended one at a time. Away from the pitch poles they are near enough that
nobody notices, but at pitch ±π/2 yaw and roll rotate about the SAME axis and only their sum is
determined: the decomposition is free to hand back (yaw, pitch, roll) or the equally correct
(yaw + π, π − pitch, roll + π), and it switches between them as an object crosses the pole.
Blending the three numbers separately across that switch sends the object through orientations at
neither end — measured at 180° partway through a single step on a cone attached nose-down, which is
a model turning inside out and snapping back once or twice a second.
So go through the quaternion, where a blend has no seams. Normalised lerp rather than a true slerp:
one step of interpolation is a small arc, the two differ by well under a pixel there, and this runs
per object per frame. Allocation-free — the caller owns out.
-
yaw0 Start yaw, radians.
-
pitch0 Start pitch.
-
roll0 Start roll.
-
yaw1 End yaw, radians.
-
pitch1 End pitch.
-
roll1 End roll.
-
a Blend factor, 0 at the start orientation and 1 at the end.
-
out Written as
[yaw, pitch, roll].
sphereVsFrustum
function sphereVsFrustum(planes: Float32Array, x: number, y: number, z: number, r: number): boolean
From @phaser-ae/kernel
Sphere-vs-frustum: true when the sphere touches the volume (conservative
— corner cases may pass a sphere that is just outside; never culls a
visible one). planes from frustumPlanes().
up
function up(yaw: number, pitch = 0): { x: number; y: number; z: number }
From @phaser-ae/kernel
Unit UP direction for a yaw/pitch — the body's own up, perpendicular to
forward. With pitch 0 it is world up (0, 1, 0); nose-down pitch
tips it forward, which is what a banking aircraft or a loop needs. For
"up the screen" in a game with no pitch, use (0, 1, 0) directly.
vec3
function vec3(x = 0, y = 0, z = 0): V3
From @phaser-ae/kernel
Creates a V3 from components. All default to 0.
viewBasis
function viewBasis(view: Mat4): { right: V3; up: V3 }
From @phaser-ae/kernel
The camera's right/up basis from a view matrix (billboards face with these).
Constants
UNIT_MESH_RADIUS
const UNIT_MESH_RADIUS: number
From @phaser-ae/kernel
Half the diagonal of a one-unit cube: the corner of every built-in primitive, and the least a mesh is culled with.
Types
DragDelta
interface DragDelta
From @phaser-ae/kernel
A frame's drag, in screen pixels. dy is positive DOWNWARD, as screens are.
dx: number— Pixels moved right.dy: number— Pixels moved down.
Mat4
type Mat4 = Float32Array
From @phaser-ae/kernel
A column-major 4x4 matrix as 16 floats (WebGPU NDC, depth 0..1).
ParentLike
interface ParentLike
From @phaser-ae/kernel
The minimal parent shape composeChain walks: a world pose plus an optional parent link.
x: number— The parent's world x position, in world units.y: number— The parent's world y position, in world units.z: number— The parent's world z position, in world units.yaw: number— The parent's yaw, in radians, clockwise from above.pitch: number— The parent's pitch, in radians.roll: number— The parent's roll, in radians.scale?: number— Uniform child-scale contribution (groups; mesh parents contribute 1).parent?: ParentLike | null— The next parent up the chain, or null at the root.
Quat
type Quat = [number, number, number, number]
From @phaser-ae/kernel
A rotation quaternion as [x, y, z, w].
TurnDelta
interface TurnDelta
From @phaser-ae/kernel
How much a control turned this frame, in radians.
dYaw: number— Yaw change, radians.dPitch: number— Pitch change, radians.
V3
interface V3
From @phaser-ae/kernel
A 3D point or vector.
x: number— The x component.y: number— The y component. Up is positive.z: number— The z component.
