LeCodesdocs

Vec2 & Vec3

The SDK's vector types. Fields are plain mutable numbers (v.x = 3 works), but every method is purea.add(b) returns a NEW vector and never touches a. The only mutators are set and copy. Both types share the same API; Vec3 adds 3D-only operations (cross product as a vector, quaternion/matrix transforms). See math value semantics.

Everywhere a vector is accepted, a raw tuple works too (Vec2Like / Vec3Like): a wrapper, a [x, y] / [x, y, z] tuple, a number[], or a Float32Array. Vectors are iterable, so they spread and destructure like tuples.

At a glance

TypeScript
const toTarget = new Vec3(target.position).sub(pos)
const dir = toTarget.normalize()                    // pure — toTarget is untouched
pos = pos.add(dir.scale(speed * dt))                // reassign; add() returns a new vector

node.position = [0, 1, 0]                           // raw tuples work wherever a Vec3 is accepted
const [x, y, z] = node.position                     // vectors destructure like tuples
if (toTarget.lengthSq() < 4) attack()               // cheaper than length() for range checks

Creating

TypeScript
new Vec2(x?, y?)                 // components; default (0, 0)
new Vec2([3, 4])                 // or copy any Vec2Like
new Vec3(x?, y?, z?)             // default (0, 0, 0)
Vec3.from(v: Vec3Like): Vec3

Vec2.zero / .one / .up / .down / .left / .right
Vec3.zero / .one / .up / .down / .left / .right / .forward / .back
Note

the static constants are getters — each access returns a fresh instance, so mutating one is safe. Vec3.forward is −Z (cameras look down −Z), Vec3.back is +Z. Vec2.up is +Y (2D world space is Y-up).

Mutation — the only two methods that change this

TypeScript
v.set(x, y): this                // Vec3: set(x, y, z)
v.copy(other: Vec2Like): this

Arithmetic (pure — each returns a new vector)

TypeScript
a.add(b)   a.sub(b)   a.mul(b)   a.div(b)     // component-wise; b may be a tuple
a.scale(s)                                     // multiply by a scalar
a.scaleAndAdd(b, s)                            // a + b·s in one call
a.negate()
a.withX(x)  a.withY(y)                         // copy with one component replaced (Vec3: withZ)

Products & measures

TypeScript
a.dot(b): number
a.length(): number          a.lengthSq(): number
a.distanceTo(b): number     a.distanceSqTo(b): number
a.angle(b): number          // angle between the vectors, RADIANS (0 if either is zero)
a.normalize(): Vec           // unit vector; a zero vector normalizes to zero

Prefer the Sq variants for comparisons — they skip the square root.

Interpolation & clamping

TypeScript
a.lerp(b, t)                 // component-wise blend, t usually 0..1
a.clamp(min, max)            // component-wise clamp between two vectors
a.min(b)   a.max(b)          // component-wise min / max

Vec2 only

TypeScript
a.cross(b): number           // scalar z of the 3D cross — signed parallelogram area
a.perp(): Vec2               // (-y, x): 90° CCW perpendicular
a.rotate(rad, origin?)       // rotate CCW by RADIANS around origin (default [0, 0])
a.heading(): number          // atan2(y, x) — the vector's angle in RADIANS

Vec3 only

TypeScript
a.cross(b): Vec3
a.reflect(normal)            // mirror about a UNIT normal: v − 2(v·n)n
a.project(onto)              // vector projection of a onto `onto` (zero `onto` → zero)
a.rotateX(rad, origin?)      // rotate the point around an axis through origin, RADIANS
a.rotateY(rad, origin?)
a.rotateZ(rad, origin?)
a.rotate(q)                  // rotate by a Quat or raw [x, y, z, w]
a.transform(m)               // transform as a POINT by a Mat4 / raw column-major length-16 array
                             // — applies translation and the perspective divide

For a direction (no translation), use Mat4.transformDirection instead of transform.

Interop

TypeScript
a.equals(b, eps? /* 1e-6 */): boolean   // relative-epsilon compare
a.clone(): Vec
a.toArray(): [number, number]           // Vec3: [number, number, number]
const [x, y] = a; [...a]                // iterable

Pitfalls

TypeScript
// ✗ expecting methods to mutate — they never do
dir.normalize()                          // discards the result; dir unchanged
dir = dir.normalize()                    // ✓ reassign

// ✗ mutating a node transform copy — getters return fresh values
node.position.x = 3                      // silent no-op (see conventions)
node.x = 3                               // ✓

See also

  • Mathf — scalar helpers (clamp, damp, smoothDamp on arrays)
  • Quat — rotations; Vec3.rotate(q) is Quat.rotateVec3 from the other side
  • Mat4 — full transforms; transformPoint / transformDirection
  • Conventions — value semantics, tuple interop