LeCodesdocs

Mat4

A 4×4 column-major matrix — the SDK's transform/projection type. Same contract as Vec3 / Quat: all methods are pure (each returns a new Mat4; only set / copy mutate), and anywhere a matrix is accepted a raw length-16 array or Float32Array works too (Mat4Like).

Unlike the vectors, the backing store is public: .m is the raw column-major number[] (length 16), yours to read and write directly.

At a glance

TypeScript
const world = Mat4.compose([0, 1, -3], Quat.fromEuler(0, 45, 0), 1.5)  // T · R · S
const p = world.transformPoint([0, 0, -1])          // local point → world
const { position, rotation, scale } = world.decompose()

const view = Mat4.lookAt([0, 2, 5], [0, 0, 0])      // camera at (0,2,5) looking at origin
const proj = Mat4.perspective(60 * DEG2RAD, w / h, 0.1, 100)
const mvp = proj.mul(view).mul(world)               // right-to-left: world, then view, then proj

The raw array: .m

TypeScript
mat.m: number[]              // length 16, column-major; translation lives in m[12..14]
mat.m[12] += vx * dt         // direct numeric surgery is fine — .m is public on purpose
mat.toFloat32Array()         // copy out as Float32Array for a host/GPU edge

The fluent methods never mutate .m — they return a new Mat4 — so writing to .m is the one way to change a matrix in place besides set / copy.

Creating

TypeScript
new Mat4()                          // identity
new Mat4(src: Mat4Like)             // copies src (a Mat4, its .m, or a raw length-16 array)
Mat4.identity(): Mat4
Mat4.from(src: Mat4Like): Mat4

Mat4.compose(position, rotation, scale? /* 1 */): Mat4  // T · R · S; scale: vector or scalar
Mat4.fromTranslation(v): Mat4
Mat4.fromScale(v /* vector or scalar */): Mat4
Mat4.fromQuat(q): Mat4
Mat4.fromEuler(x, y, z, order? /* "YXZ" */): Mat4       // angles in DEGREES

Combining

TypeScript
a.mul(b): Mat4               // a · b — applies b FIRST, then a (right-to-left, like glsl)
a.premul(b): Mat4            // b · a — the other side
a.invert(): Mat4             // singular matrices return identity
a.transpose(): Mat4
a.determinant(): number
a.equals(b, eps? /* 1e-6 */): boolean

Building transforms (local-space, post-multiply)

TypeScript
m.translate(v): Mat4              // m · T(v)
m.rotate(rad, axis): Mat4         // m · R(axis, rad) — angle in RADIANS, axis normalized for you
m.rotateX(rad)  m.rotateY(rad)  m.rotateZ(rad)
m.scale(v /* vector or scalar */): Mat4

Each applies its transform in the matrix's local space (post-multiplication), so a chain reads like a scene-graph descent: Mat4.fromTranslation(pos).rotateY(a).scale(2).

Transforming vectors

TypeScript
m.transformPoint(v): Vec3         // full transform: translation + perspective divide
m.transformDirection(v): Vec3     // rotation/scale only — no translation, no divide

Vec3.transform(m) is transformPoint from the vector's side.

Decomposition

TypeScript
m.position: Vec3                  // getter — translation column (m[12..14])
m.scaling: Vec3                   // per-axis scale (basis vector lengths)
m.rotation: Quat                  // rotation with scale divided out
m.eulerAngles: Vec3               // DEGREES, "YXZ" order
m.toEuler(order? /* "YXZ" */): Vec3
m.decompose(): { position: Vec3, rotation: Quat, scale: Vec3 }
m.basisX / m.basisY / m.basisZ: Vec3   // the local axes in world space (columns 0/1/2)

All getters return fresh values — mutating m.position does not write back into the matrix (write m.m[12..14] directly for that).

Cameras & projection

TypeScript
Mat4.lookAt(eye, center, up? /* Vec3.up */): Mat4     // a VIEW matrix (world → camera)
Mat4.targetTo(eye, target, up? /* Vec3.up */): Mat4   // a WORLD matrix placing an object at eye,
                                                      // oriented to face target
Mat4.perspective(fovy, aspect, near, far): Mat4       // fovy in RADIANS; far may be Infinity
Mat4.ortho(left, right, bottom, top, near, far): Mat4
Note

projections use the WebGL/OpenGL clip convention (NDC z ∈ [−1, 1]). lookAt vs targetTo is the classic trap: lookAt builds the inverse (view) matrix; to orient a node toward something, you want targetTo (or Quat.lookRotation).

Interop

TypeScript
m.set(values: ArrayLike<number>): this   // the only mutators (besides writing .m)
m.copy(src: Mat4Like): this
m.clone(): Mat4
m.toArray(): number[]                    // copy of .m
m.toFloat32Array(): Float32Array

Pitfalls

TypeScript
// ✗ degrees into rotate/perspective — raw-angle arguments are radians
m.rotateY(90);            Mat4.perspective(60, aspect, 0.1, 100)
m.rotateY(90 * DEG2RAD);  Mat4.perspective(60 * DEG2RAD, aspect, 0.1, 100)   // ✓

// ✗ treating .m as row-major — it's column-major; translation is m[12], m[13], m[14]
m.m[3] = x
m.m[12] = x   // ✓

See also

  • Vec2 & Vec3Vec3.transform(m), basis directions
  • QuatQuat.fromEuler / lookRotation; mat.rotation round-trips with Mat4.fromQuat
  • Conventions — degrees vs radians, −Z forward, Y-up