linalg
stableLinear algebra library providing 2D, 3D, and 4D vectors, 3x3 and 4x4 matrices, quaternions, and common graphics transforms like perspective, look-at, and quaternion slerp.
use plugin linalg::{vec2, vec2_add, vec2_sub, …} Functions (57)
- vec2 Create a 2D vector
- vec2_add Add two vec2 values
- vec2_sub Subtract two vec2 values
- vec2_dot Dot product of two vec2 values
- vec2_length Magnitude of a vec2
- vec2_normalize Unit-length vec2
- vec2_rotate Rotate a vec2 by angle in radians
- vec2_perpendicular 90-degree perpendicular vec2
- vec2_scale Multiply vec2 by a scalar
- vec2_distance Distance between two vec2 points
- vec2_lerp Linear interpolation between two vec2 values
- vec3 Create a 3D vector
- vec3_add Add two vec3 values
- vec3_sub Subtract two vec3 values
- vec3_dot Dot product of two vec3 values
- vec3_cross Cross product of two vec3 values
- vec3_normalize Unit-length vec3
- vec3_length Magnitude of a vec3
- vec3_scale Multiply vec3 by a scalar
- vec3_distance Distance between two vec3 points
- vec3_lerp Linear interpolation between two vec3 values
- vec3_negate Negate all components of a vec3
- vec3_angle Angle in radians between two vec3 values
- vec4 Create a 4D vector
- vec4_add Add two vec4 values
- vec4_sub Subtract two vec4 values
- vec4_dot Dot product of two vec4 values
- vec4_normalize Unit-length vec4
- vec4_length Magnitude of a vec4
- vec4_scale Multiply vec4 by a scalar
- vec4_lerp Linear interpolation between two vec4 values
- project Project vec3 onto another vec3
- reflect Reflect vec3 around a surface normal
- mat3_identity Create a 3x3 identity matrix
- mat3_mul Multiply two 3x3 matrices
- mat3_transpose Transpose a 3x3 matrix
- mat3_inverse Invert a 3x3 matrix
- mat3_determinant Determinant of a 3x3 matrix
- mat4_identity Create a 4x4 identity matrix
- mat4_mul Multiply two 4x4 matrices
- mat4_translation Translation matrix from x, y, z
- mat4_rotation_x Rotation matrix around X axis
- mat4_rotation_y Rotation matrix around Y axis
- mat4_rotation_z Rotation matrix around Z axis
- mat4_scale Scale matrix from x, y, z
- mat4_perspective Perspective projection matrix
- mat4_orthographic Orthographic projection matrix
- mat4_look_at Camera view matrix
- mat4_transpose Transpose a 4x4 matrix
- mat4_inverse Invert a 4x4 matrix
- mat4_determinant Determinant of a 4x4 matrix
- mat4_from_quat Rotation matrix from quaternion components
- quat_from_euler Quaternion from pitch, yaw, roll
- quat_mul Multiply two quaternions
- quat_slerp Spherical interpolation between quaternions
- transform_point Apply mat4 to a point (with translation)
- transform_direction Apply mat4 to a direction (no translation)
Overview
linalg is a dependency-free math toolkit for 2D, 3D, and 4D graphics. It has no
opaque handles or hidden state: a vector is an ordinary table ({x, y}, {x, y, z},
or {x, y, z, w}), a matrix is a flat array table of 9 or 16 numbers in
column-major order, and a quaternion is just a vec4 {x, y, z, w}. Every function
takes plain tables and returns plain tables, so values are easy to store, pass
around, and inspect by field.
The core concept is composition. Build vectors with vec2 / vec3 / vec4,
build transform matrices with the mat4_* constructors (mat4_translation,
mat4_rotation_y, mat4_perspective, mat4_look_at, ...), chain them together
with mat4_mul, and finally apply the result to geometry with transform_point
and transform_direction. Quaternion helpers (quat_from_euler, quat_mul,
quat_slerp, mat4_from_quat) cover smooth rotation. Use it whenever you need
camera math, model transforms, normals, projections, or interpolation without
pulling in an external linear-algebra dependency.
A few invariants are worth keeping in mind: matrices are column-major, rotation
angles are in radians, and functions like vec3_normalize, mat4_inverse, and
mat4_look_at raise an error on degenerate input (zero vectors, singular
matrices, or an eye point equal to the center) rather than returning garbage.
Common patterns
Build a model-view-projection matrix and transform a point with it:
use plugin linalg::{vec3, mat4_perspective, mat4_look_at, mat4_translation, mat4_mul, transform_point}
let proj = mat4_perspective(1.0472, 1.7778, 0.1, 1000.0)
let view = mat4_look_at(vec3(0.0, 5.0, 10.0), vec3(0.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0))
let model = mat4_translation(2.0, 0.0, 0.0)
let mvp = mat4_mul(mat4_mul(proj, view), model)
let screen = transform_point(mvp, vec3(0.0, 0.0, 0.0))
print("x={screen["x"]} y={screen["y"]} z={screen["z"]}")
Convert Euler angles to a quaternion, turn it into a rotation matrix, and rotate a direction:
use plugin linalg::{vec3, quat_from_euler, mat4_from_quat, transform_direction}
let q = quat_from_euler(0.0, 1.5707963, 0.0)
let rot = mat4_from_quat(q["x"], q["y"], q["z"], q["w"])
let facing = transform_direction(rot, vec3(0.0, 0.0, -1.0))
print("facing: {facing["x"]}, {facing["y"]}, {facing["z"]}")
Compute a triangle's surface normal from its three corners:
use plugin linalg::{vec3, vec3_sub, vec3_cross, vec3_normalize}
let a = vec3(0.0, 0.0, 0.0)
let b = vec3(1.0, 0.0, 0.0)
let c = vec3(0.0, 1.0, 0.0)
let normal = vec3_normalize(vec3_cross(vec3_sub(b, a), vec3_sub(c, a)))
print("normal: {normal["x"]}, {normal["y"]}, {normal["z"]}")
Create a 2D vector
Creates a 2D vector table with x and y fields.
use plugin linalg::{vec2, vec2_add, vec2_length}
let a = vec2(3.0, 4.0)
let b = vec2(1.0, 0.0)
let sum = vec2_add(a, b)
print("Length: {vec2_length(a)}")
Add two vec2 values
Adds two vec2 tables component-wise. Returns a new vec2.
use plugin linalg::{vec2, vec2_add}
let result = vec2_add(vec2(1.0, 2.0), vec2(3.0, 4.0))
Subtract two vec2 values
Subtracts b from a component-wise.
use plugin linalg::{vec2, vec2_sub}
let delta = vec2_sub(vec2(5.0, 5.0), vec2(2.0, 1.0))
Dot product of two vec2 values
Returns the scalar dot product of two vec2 values.
use plugin linalg::{vec2, vec2_dot}
let d = vec2_dot(vec2(1.0, 0.0), vec2(0.0, 1.0))
Magnitude of a vec2
Returns the Euclidean length (magnitude) of a vec2.
use plugin linalg::{vec2, vec2_length}
print(vec2_length(vec2(3.0, 4.0)))
Unit-length vec2
Returns a unit-length copy of v. Errors if v is the zero vector.
use plugin linalg::{vec2, vec2_normalize}
let n = vec2_normalize(vec2(3.0, 4.0))
Rotate a vec2 by angle in radians
Rotates v by angle radians counter-clockwise.
use plugin linalg::{vec2, vec2_rotate}
let rotated = vec2_rotate(vec2(1.0, 0.0), 1.5707963)
90-degree perpendicular vec2
Returns a vector perpendicular to v (rotated 90 degrees counter-clockwise): (-y, x).
use plugin linalg::{vec2, vec2_perpendicular}
let perp = vec2_perpendicular(vec2(1.0, 0.0))
Multiply vec2 by a scalar
Multiplies each component of v by scalar s.
use plugin linalg::{vec2, vec2_scale}
let doubled = vec2_scale(vec2(1.0, 2.0), 2.0)
Distance between two vec2 points
Returns the Euclidean distance between two vec2 points.
use plugin linalg::{vec2, vec2_distance}
let d = vec2_distance(vec2(0.0, 0.0), vec2(3.0, 4.0))
Linear interpolation between two vec2 values
Linearly interpolates between a and b by factor t (0.0 = a, 1.0 = b).
use plugin linalg::{vec2, vec2_lerp}
let mid = vec2_lerp(vec2(0.0, 0.0), vec2(10.0, 10.0), 0.5)
Sample the path between two points to animate a position over several frames:
use plugin linalg::{vec2, vec2_lerp}
let from = vec2(0.0, 0.0)
let to = vec2(100.0, 50.0)
for i in 0..5 {
let p = vec2_lerp(from, to, i / 4.0)
print("frame {i}: ({p["x"]}, {p["y"]})")
}
Create a 3D vector
Creates a 3D vector table with x, y, and z fields.
use plugin linalg::{vec3, vec3_cross, vec3_normalize}
let up = vec3(0.0, 1.0, 0.0)
let fwd = vec3(0.0, 0.0, -1.0)
let right = vec3_normalize(vec3_cross(fwd, up))
Add two vec3 values
Adds two vec3 tables component-wise.
use plugin linalg::{vec3, vec3_add}
let sum = vec3_add(vec3(1.0, 2.0, 3.0), vec3(4.0, 5.0, 6.0))
Subtract two vec3 values
Subtracts b from a component-wise.
use plugin linalg::{vec3, vec3_sub}
let diff = vec3_sub(vec3(5.0, 5.0, 5.0), vec3(1.0, 2.0, 3.0))
Dot product of two vec3 values
Returns the scalar dot product of two vec3 values. Useful for computing angles and projections.
use plugin linalg::{vec3, vec3_dot}
let d = vec3_dot(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0))
Cross product of two vec3 values
Returns the cross product of a and b, a vector perpendicular to both. Commonly used to compute surface normals.
use plugin linalg::{vec3, vec3_cross}
let normal = vec3_cross(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0))
Build an orthonormal basis: derive the camera's right vector from forward and up.
use plugin linalg::{vec3, vec3_cross, vec3_normalize}
let forward = vec3(0.0, 0.0, -1.0)
let up = vec3(0.0, 1.0, 0.0)
let right = vec3_normalize(vec3_cross(forward, up))
print("right: {right["x"]}, {right["y"]}, {right["z"]}")
Unit-length vec3
Returns a unit-length copy of v. Errors if v is the zero vector.
use plugin linalg::{vec3, vec3_normalize}
let n = vec3_normalize(vec3(1.0, 2.0, 2.0))
Magnitude of a vec3
Returns the Euclidean length of a vec3.
use plugin linalg::{vec3, vec3_length}
print(vec3_length(vec3(1.0, 2.0, 2.0)))
Multiply vec3 by a scalar
Multiplies each component of v by scalar s.
use plugin linalg::{vec3, vec3_scale}
let big = vec3_scale(vec3(1.0, 1.0, 1.0), 10.0)
Distance between two vec3 points
Returns the Euclidean distance between two vec3 points.
use plugin linalg::{vec3, vec3_distance}
let d = vec3_distance(vec3(0.0, 0.0, 0.0), vec3(1.0, 1.0, 1.0))
Linear interpolation between two vec3 values
Linearly interpolates between vec3 a and b by factor t.
use plugin linalg::{vec3, vec3_lerp}
let mid = vec3_lerp(vec3(0.0, 0.0, 0.0), vec3(10.0, 0.0, 0.0), 0.5)
Negate all components of a vec3
Returns a vec3 with all components negated.
use plugin linalg::{vec3, vec3_negate}
let neg = vec3_negate(vec3(1.0, -2.0, 3.0))
Angle in radians between two vec3 values
Returns the angle in radians between two vec3 values. Errors if either vector is zero.
use plugin linalg::{vec3, vec3_angle}
let angle = vec3_angle(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0))
Create a 4D vector
Creates a 4D vector table with x, y, z, and w fields. Used for homogeneous coordinates and quaternions.
use plugin linalg::{vec4}
let v = vec4(0.0, 0.0, 0.0, 1.0)
Add two vec4 values
Adds two vec4 tables component-wise.
use plugin linalg::{vec4, vec4_add}
let sum = vec4_add(vec4(1.0, 2.0, 3.0, 4.0), vec4(1.0, 1.0, 1.0, 1.0))
Subtract two vec4 values
Subtracts b from a component-wise.
use plugin linalg::{vec4, vec4_sub}
let diff = vec4_sub(vec4(4.0, 3.0, 2.0, 1.0), vec4(1.0, 1.0, 1.0, 1.0))
Dot product of two vec4 values
Returns the scalar dot product of two vec4 values.
use plugin linalg::{vec4, vec4_dot}
let d = vec4_dot(vec4(1.0, 0.0, 0.0, 0.0), vec4(1.0, 0.0, 0.0, 0.0))
Unit-length vec4
Returns a unit-length copy of v. Errors if v is the zero vector.
use plugin linalg::{vec4, vec4_normalize}
let n = vec4_normalize(vec4(1.0, 2.0, 3.0, 4.0))
Magnitude of a vec4
Returns the Euclidean length of a vec4.
use plugin linalg::{vec4, vec4_length}
print(vec4_length(vec4(1.0, 0.0, 0.0, 0.0)))
Multiply vec4 by a scalar
Multiplies each component of v by scalar s.
use plugin linalg::{vec4, vec4_scale}
let scaled = vec4_scale(vec4(1.0, 2.0, 3.0, 4.0), 0.5)
Linear interpolation between two vec4 values
Linearly interpolates between vec4 a and b by factor t.
use plugin linalg::{vec4, vec4_lerp}
let mid = vec4_lerp(vec4(0.0, 0.0, 0.0, 0.0), vec4(1.0, 1.0, 1.0, 1.0), 0.5)
Project vec3 onto another vec3
Projects vec3 v onto vec3 onto. Returns the component of v parallel to onto. Errors if onto is the zero vector.
use plugin linalg::{vec3, project}
let shadow = project(vec3(3.0, 4.0, 0.0), vec3(1.0, 0.0, 0.0))
Reflect vec3 around a surface normal
Reflects vec3 v around surface normal. The normal should be unit-length for correct results.
use plugin linalg::{vec3, reflect}
let bounced = reflect(vec3(1.0, -1.0, 0.0), vec3(0.0, 1.0, 0.0))
Bounce an incoming velocity off a wall, normalizing the surface normal first:
use plugin linalg::{vec3, vec3_normalize, reflect}
let velocity = vec3(3.0, -2.0, 0.0)
let wall = vec3_normalize(vec3(0.0, 1.0, 0.0))
let outgoing = reflect(velocity, wall)
print("bounced y: {outgoing["y"]}")
Create a 3x3 identity matrix
Returns a 3x3 identity matrix as a 9-element array table (column-major order).
use plugin linalg::{mat3_identity}
let m = mat3_identity()
Multiply two 3x3 matrices
Multiplies two 3x3 matrices (column-major). Returns the product as a 9-element table.
use plugin linalg::{mat3_identity, mat3_mul}
let m = mat3_identity()
let result = mat3_mul(m, m)
Transpose a 3x3 matrix
Returns the transpose of a 3x3 matrix.
use plugin linalg::{mat3_identity, mat3_transpose}
let t = mat3_transpose(mat3_identity())
Invert a 3x3 matrix
Returns the inverse of a 3x3 matrix. Errors if the matrix is singular (determinant near zero).
use plugin linalg::{mat3_identity, mat3_inverse}
let inv = mat3_inverse(mat3_identity())
Determinant of a 3x3 matrix
Returns the scalar determinant of a 3x3 matrix.
use plugin linalg::{mat3_identity, mat3_determinant}
print(mat3_determinant(mat3_identity()))
Create a 4x4 identity matrix
Returns a 4x4 identity matrix as a 16-element array table (column-major order).
use plugin linalg::{mat4_identity}
let m = mat4_identity()
Multiply two 4x4 matrices
Multiplies two 4x4 matrices (column-major). Use this to combine transforms: mat4_mul(projection, view).
use plugin linalg::{mat4_identity, mat4_translation, mat4_mul}
let model = mat4_translation(1.0, 0.0, 0.0)
let mvp = mat4_mul(mat4_identity(), model)
Compose translate-then-rotate into a single transform. Remember matrix multiplication applies right-to-left, so the rightmost matrix acts first:
use plugin linalg::{mat4_translation, mat4_rotation_z, mat4_mul, transform_point, vec3}
let rotate = mat4_rotation_z(1.5707963)
let translate = mat4_translation(10.0, 0.0, 0.0)
let combined = mat4_mul(translate, rotate)
let p = transform_point(combined, vec3(1.0, 0.0, 0.0))
print("({p["x"]}, {p["y"]})")
Translation matrix from x, y, z
Returns a 4x4 translation matrix that moves by (x, y, z).
use plugin linalg::{mat4_translation, transform_point, vec3}
let m = mat4_translation(5.0, 0.0, 0.0)
let moved = transform_point(m, vec3(0.0, 0.0, 0.0))
Rotation matrix around X axis
Returns a 4x4 rotation matrix around the X axis by angle_rad radians.
use plugin linalg::{mat4_rotation_x}
let rx = mat4_rotation_x(1.5707963)
Rotation matrix around Y axis
Returns a 4x4 rotation matrix around the Y axis by angle_rad radians.
use plugin linalg::{mat4_rotation_y}
let ry = mat4_rotation_y(3.14159)
Rotation matrix around Z axis
Returns a 4x4 rotation matrix around the Z axis by angle_rad radians.
use plugin linalg::{mat4_rotation_z}
let rz = mat4_rotation_z(0.7854)
Scale matrix from x, y, z
Returns a 4x4 scale matrix with scale factors (x, y, z).
use plugin linalg::{mat4_scale}
let s = mat4_scale(2.0, 2.0, 2.0)
Perspective projection matrix
Returns a perspective projection matrix. fov_rad is the vertical field of view in radians, aspect is width/height.
use plugin linalg::{mat4_perspective}
let proj = mat4_perspective(1.0472, 1.7778, 0.1, 1000.0)
Orthographic projection matrix
Returns an orthographic projection matrix for 2D or isometric rendering.
use plugin linalg::{mat4_orthographic}
let ortho = mat4_orthographic(-400.0, 400.0, -300.0, 300.0, -1.0, 1.0)
Camera view matrix
Returns a camera view matrix. eye, center, and up are vec3 tables. Errors if eye equals center or if forward is parallel to up.
use plugin linalg::{vec3, mat4_look_at}
let view = mat4_look_at(vec3(0.0, 5.0, 10.0), vec3(0.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0))
Transpose a 4x4 matrix
Returns the transpose of a 4x4 matrix.
use plugin linalg::{mat4_identity, mat4_transpose}
let t = mat4_transpose(mat4_identity())
Invert a 4x4 matrix
Returns the inverse of a 4x4 matrix using cofactor expansion. Errors if the matrix is singular.
use plugin linalg::{mat4_translation, mat4_inverse}
let m = mat4_translation(3.0, 0.0, 0.0)
let inv = mat4_inverse(m)
Determinant of a 4x4 matrix
Returns the scalar determinant of a 4x4 matrix.
use plugin linalg::{mat4_identity, mat4_determinant}
print(mat4_determinant(mat4_identity()))
Rotation matrix from quaternion components
Converts a quaternion (qx, qy, qz, qw) to a 4x4 rotation matrix.
use plugin linalg::{mat4_from_quat}
let rot = mat4_from_quat(0.0, 0.0, 0.0, 1.0)
Quaternion from pitch, yaw, roll
Converts Euler angles (pitch, yaw, roll) in radians to a quaternion, returned as a vec4 table {x, y, z, w}.
use plugin linalg::{quat_from_euler, mat4_from_quat}
let q = quat_from_euler(0.0, 1.5707963, 0.0)
let rot = mat4_from_quat(q["x"], q["y"], q["z"], q["w"])
Multiply two quaternions
Multiplies two quaternions (vec4 tables). Returns the composed rotation as a vec4.
use plugin linalg::{quat_from_euler, quat_mul}
let a = quat_from_euler(0.0, 0.5, 0.0)
let b = quat_from_euler(0.0, 0.5, 0.0)
let combined = quat_mul(a, b)
Spherical interpolation between quaternions
Spherically interpolates between quaternions q1 and q2 by factor t (0.0 = q1, 1.0 = q2). Automatically takes the shortest path.
use plugin linalg::{quat_from_euler, quat_slerp}
let start = quat_from_euler(0.0, 0.0, 0.0)
let end_q = quat_from_euler(0.0, 1.5707963, 0.0)
let halfway = quat_slerp(start, end_q, 0.5)
Animate a smooth turn and convert each step into a usable rotation matrix:
use plugin linalg::{quat_from_euler, quat_slerp, mat4_from_quat}
let a = quat_from_euler(0.0, 0.0, 0.0)
let b = quat_from_euler(0.0, 3.14159, 0.0)
for i in 0..4 {
let q = quat_slerp(a, b, i / 3.0)
let m = mat4_from_quat(q["x"], q["y"], q["z"], q["w"])
print("step {i} ready")
}
Apply mat4 to a point (with translation)
Applies a 4x4 matrix to a vec3 point, including translation (homogeneous w=1). Returns the transformed vec3.
use plugin linalg::{mat4_translation, vec3, transform_point}
let m = mat4_translation(10.0, 0.0, 0.0)
let p = transform_point(m, vec3(0.0, 0.0, 0.0))
print("Moved to: {p["x"]}")
Apply mat4 to a direction (no translation)
Applies a 4x4 matrix to a vec3 direction, ignoring translation (homogeneous w=0). Use for normals and direction vectors.
use plugin linalg::{mat4_rotation_y, vec3, transform_direction}
let rot = mat4_rotation_y(1.5707963)
let fwd = transform_direction(rot, vec3(0.0, 0.0, -1.0))
Unlike transform_point, a pure translation leaves a direction unchanged because
the translation column is ignored:
use plugin linalg::{mat4_translation, vec3, transform_direction}
let move = mat4_translation(100.0, 100.0, 100.0)
let dir = transform_direction(move, vec3(0.0, 0.0, 1.0))
print("unchanged: {dir["x"]}, {dir["y"]}, {dir["z"]}")