Core Types
Essential data types for positions, colors, and 2D/3D transformations.
Vector
Vector type for positions, directions, and sizes. Vector.xy creates a 2D vector; the runtime also supports a z component for 3D math. Components are read-only and operations return new vectors.
Note: Some examples may show
Vec2Dwhich is an alias forVector.
Constructors
Vector.xy(x, y)
Creates a new 2D vector.
Vector.xy(x: number, y: number): Vector
Parameters:
| Parameter | Type | Description |
|---|---|---|
| x | number | X component |
| y | number | Y component |
Returns: Vector
Example:
local position = Vector.xy(100, 50)
local direction = Vector.xy(1, 0) -- Unit vector pointing right
Vector.xyz(x, y, z)
Creates a vector with an explicit z component. The API remains present in the
accepted runtime-v0.1.344 target.
Vector.xyz(x: number, y: number, z: number): Vector
local normal = Vector.xyz(0, 0, 1)
Focused validation in Rive Beta 0.8.5390 (build 5377) passed Vector.xyz,
Vector.cross3, and vector buffer writes. This confirms the selected editor
lane. Exact-target source confirmation and that editor execution remain
separate evidence.
Vector.origin()
Returns the zero vector (0, 0).
Vector.origin(): Vector
Attributes
vec.x
The X component. Type: number (read-only)
vec.y
The Y component. Type: number (read-only)
vec.z
The Z component. Type: number (read-only)
Always 0 for vectors created with Vector.xy(). Populated by 3D APIs that return a Vector, such as Mat4:transformPoint. Editor-validated: Vector.xy(1, 2).z returns 0.
Indexing
Vectors support indexed access:
local v = Vector.xy(10, 20)
print(v[1]) -- 10 (x component)
print(v[2]) -- 20 (y component)
print(v[3]) -- 0 (z component)
Static Methods (Preferred)
Important: Use static methods instead of instance methods. Instance methods are deprecated.
Vector.length(v)
Returns the magnitude (length) of the vector.
Vector.length(v: Vector): number
Example:
local v = Vector.xy(3, 4)
print(Vector.length(v)) -- 5 (3-4-5 triangle)
Vector.lengthSquared(v)
Returns the squared length. Faster than length() when you only need to compare magnitudes.
Vector.lengthSquared(v: Vector): number
Vector.normalized(v)
Returns a unit vector (length 1) pointing in the same direction. Returns zero vector if length is zero.
Vector.normalized(v: Vector): Vector
Example:
local velocity = Vector.xy(10, 0)
local direction = Vector.normalized(velocity) -- Vector(1, 0)
Vector.distance(a, b)
Returns the distance between two vectors.
Vector.distance(a: Vector, b: Vector): number
Example:
local d = Vector.distance(Vector.xy(0, 0), Vector.xy(3, 4)) -- 5
Vector.distanceSquared(a, b)
Returns the squared distance. Faster for comparisons.
Vector.distanceSquared(a: Vector, b: Vector): number
Vector.dot(a, b)
Returns the dot product. Useful for angle calculations and projections.
Vector.dot(a: Vector, b: Vector): number
Example:
local a = Vector.xy(1, 0)
local b = Vector.xy(0, 1)
print(Vector.dot(a, b)) -- 0 (perpendicular vectors)
Vector.cross(a, b)
Returns the z-component of the 3D cross product (the 2D "signed area").
Vector.cross(a: Vector, b: Vector): number
Example:
local c = Vector.cross(Vector.xy(1, 0), Vector.xy(0, 1)) -- 1
When to use: Determining winding order (clockwise vs counter-clockwise), computing signed areas, checking which side of a line a point is on.
Vector.cross3(a, b)
Returns the 3D vector cross product. Do not substitute this for Vector.cross:
cross returns the scalar z-component used by 2D winding/math, while cross3
returns a Vector.
Vector.cross3(a: Vector, b: Vector): Vector
local normal = Vector.cross3(Vector.xyz(1, 0, 0), Vector.xyz(0, 1, 0))
-- Vector(0, 0, 1)
Buffer writes
The target runtime can write vector components as float32 values.
writeToBuffer writes 12 bytes (x, y, z) and writeVec4 writes 16
bytes (x, y, z, w). Both validate destination bounds.
vector:writeToBuffer(destination: buffer, byteOffset: number): ()
vector:writeVec4(destination: buffer, byteOffset: number, w: number): ()
local bytes = buffer.create(32)
Vector.xyz(1, 2, 3):writeToBuffer(bytes, 4) -- bytes 4..15
Vector.xyz(1, 2, 3):writeVec4(bytes, 16, 1) -- bytes 16..31
Writing past the destination reports an error rather than silently truncating; cover both valid layouts and negative bounds cases in shader tests.
Vector.scaleAndAdd(a, b, scale)
Returns a + b * scale. Avoids creating an intermediate vector.
Vector.scaleAndAdd(a: Vector, b: Vector, scale: number): Vector
Example:
-- Move position by velocity * dt
local newPos = Vector.scaleAndAdd(position, velocity, deltaTime)
Vector.scaleAndSub(a, b, scale)
Returns a - b * scale.
Vector.scaleAndSub(a: Vector, b: Vector, scale: number): Vector
Vector.lerp(from, to, t)
Linear interpolation between two vectors.
Vector.lerp(from: Vector, to: Vector, t: number): Vector
Parameters:
| Parameter | Type | Description |
|---|---|---|
| from | Vector | Start vector (t=0) |
| to | Vector | Target vector (t=1) |
| t | number | Interpolation factor (0-1) |
Example:
local midpoint = Vector.lerp(Vector.xy(0, 0), Vector.xy(100, 100), 0.5)
-- Vector(50, 50)
Deprecated Instance Methods
The following instance methods still work but are deprecated. Use the static Vector.* versions above for better performance.
| Deprecated | Use Instead |
|---|---|
vec:length() | Vector.length(vec) |
vec:lengthSquared() | Vector.lengthSquared(vec) |
vec:normalized() | Vector.normalized(vec) |
vec:distance(other) | Vector.distance(vec, other) |
vec:distanceSquared(other) | Vector.distanceSquared(vec, other) |
vec:dot(other) | Vector.dot(vec, other) |
vec:lerp(other, t) | Vector.lerp(vec, other, t) |
Operators
| Operator | Description | Example |
|---|---|---|
+ | Addition | vec1 + vec2 |
- | Subtraction | vec1 - vec2 |
* | Scalar multiplication | vec * 2 |
/ | Scalar division | vec / 2 |
- (unary) | Negation | -vec |
== | Equality | vec1 == vec2 |
Example:
local a = Vector.xy(10, 20)
local b = Vector.xy(5, 10)
local sum = a + b -- Vector(15, 30)
local diff = a - b -- Vector(5, 10)
local scaled = a * 2 -- Vector(20, 40)
local divided = a / 2 -- Vector(5, 10)
local negated = -a -- Vector(-10, -20)
Color
RGBA color with 0-255 channel values. Colors are accessed and modified via static functions, not properties.
Constructors
Color.rgba(r, g, b, a)
Creates a color with alpha.
Color.rgba(r: number, g: number, b: number, a: number): Color
Parameters:
| Parameter | Type | Description |
|---|---|---|
| r | number | Red (0-255) |
| g | number | Green (0-255) |
| b | number | Blue (0-255) |
| a | number | Alpha (0-255, 255 = opaque) |
Example:
local red = Color.rgba(255, 0, 0, 255)
local semiTransparent = Color.rgba(0, 0, 255, 128)
Color.rgb(r, g, b)
Creates an opaque color (alpha = 255).
Color.rgb(r: number, g: number, b: number): Color
Static Channel Accessors
Important: Color channels are accessed via static functions, not properties. Use
Color.red(c)notc.r.
Color.red(color [, value])
Gets the red channel, or returns a new color with the red channel updated.
Color.red(color: Color): number -- Get red
Color.red(color: Color, value: number): Color -- Set red (returns new color)
Color.green(color [, value])
Gets the green channel, or returns a new color with the green channel updated.
Color.green(color: Color): number
Color.green(color: Color, value: number): Color
Color.blue(color [, value])
Gets the blue channel, or returns a new color with the blue channel updated.
Color.blue(color: Color): number
Color.blue(color: Color, value: number): Color
Color.alpha(color [, value])
Gets the alpha channel (0-255), or returns a new color with the alpha channel updated.
Color.alpha(color: Color): number
Color.alpha(color: Color, value: number): Color
Color.opacity(color [, value])
Gets the opacity as normalized value (0.0-1.0), or returns a new color with opacity set.
Color.opacity(color: Color): number
Color.opacity(color: Color, value: number): Color
Example:
local c = Color.rgb(255, 128, 0)
-- Get channels
local r = Color.red(c) -- 255
local g = Color.green(c) -- 128
local op = Color.opacity(c) -- 1.0
-- Create modified copies (colors are immutable)
local darker = Color.red(c, 128) -- Returns new color with red=128
local faded = Color.opacity(c, 0.5) -- Returns new color with 50% opacity
Static Methods
Color.lerp(from, to, t)
Interpolates between two colors.
Color.lerp(from: Color, to: Color, t: number): Color
Parameters:
| Parameter | Type | Description |
|---|---|---|
| from | Color | Start color (t=0) |
| to | Color | End color (t=1) |
| t | number | Interpolation factor (0-1) |
Example:
local startColor = Color.rgb(255, 0, 0) -- Red
local endColor = Color.rgb(0, 0, 255) -- Blue
local purple = Color.lerp(startColor, endColor, 0.5)
Color.toFloat(color)
Converts a Color (ARGB packed integer) to an {r, g, b, a} array table with values in the 0.0–1.0 range. Primarily used with clearColor in GPU render passes.
Color.toFloat(color: Color): { number }
Example:
local cc = Color.toFloat(Color.rgba(255, 0, 0, 128))
-- cc = { 1.0, 0.0, 0.0, 0.502 }
Editor-validated 2026-06-11: type(Color.toFloat) is function; the example above returns exactly {1, 0, 0, 0.5019607843137255}.
See GPU Shaders for usage in render pass descriptors.
Runtime Representation
A Color is a plain Luau number at runtime — an ARGB packed integer. type(Color.rgb(255, 0, 0)) returns "number", and raw packed values such as 0xFFFF0000 are accepted anywhere a Color is expected (0xFFFF0000 == Color.rgb(255, 0, 0)). Prefer the Color.* constructors for readability, but raw hex literals are not an error.
See Also: Paint, Gradient, GradientStop
Mat2D
2D affine transformation matrix for translation, rotation, and scale.
Fields
mat.xx
X scale component. Type: number
mat.xy
X shear component. Type: number
mat.yx
Y shear component. Type: number
mat.yy
Y scale component. Type: number
mat.tx
X translation component. Type: number
mat.ty
Y translation component. Type: number
Constructors
Mat2D.values(xx, xy, yx, yy, tx, ty)
Creates a matrix with explicit components.
Mat2D.values(xx: number, xy: number, yx: number, yy: number, tx: number, ty: number): Mat2D
Mat2D.identity()
Creates an identity matrix (no transformation).
Mat2D.identity(): Mat2D
Mat2D.withTranslation(x, y)
Creates a translation matrix.
Mat2D.withTranslation(pos: Vector): Mat2D
Mat2D.withTranslation(x: number, y: number): Mat2D
Example:
local moveRight = Mat2D.withTranslation(100, 0)
Mat2D.withRotation(radians)
Creates a rotation matrix.
Mat2D.withRotation(radians: number): Mat2D
Example:
local rotate90 = Mat2D.withRotation(math.rad(90))
local rotate45 = Mat2D.withRotation(math.pi / 4)
Mat2D.withScale(sx, sy)
Creates a scale matrix.
Mat2D.withScale(scale: Vector): Mat2D
Mat2D.withScale(sx: number, sy: number): Mat2D
Mat2D.withScaleAndTranslation(scale, translation)
Creates a scale+translation matrix from numeric values or vectors.
Mat2D.withScaleAndTranslation(scale: Vector, translation: Vector): Mat2D
Mat2D.withScaleAndTranslation(sx: number, sy: number, tx: number, ty: number): Mat2D
Methods
mat:invert()
Returns the inverse matrix, or nil if the matrix is not invertible. Useful for converting between coordinate spaces.
mat:invert(): Mat2D?
mat:isIdentity()
Returns true if the matrix is the identity transform.
mat:isIdentity(): boolean
Static Methods
Mat2D.invert(output, input)
Static version that writes into an existing matrix (avoids allocation). Returns true if invertible.
Mat2D.invert(output: Mat2D, input: Mat2D): boolean
Example:
local inv = Mat2D.identity()
if Mat2D.invert(inv, worldTransform) then
local localPt = inv * worldPt
end
Operators
Matrix / Vector Multiplication (*)
Transforms a vector by the matrix.
Mat2D * Vector -> Vector
local p = Mat2D.withTranslation(10, 5) * Vector.xy(1, 2)
Matrix Multiplication (*)
Combines transformations. Order matters!
Mat2D * Mat2D -> Mat2D
local combined = mat1 * mat2 -- Apply mat2 first, then mat1
Equality (==)
Returns true if all components are equal.
The August 14 Rive Beta MCP runtime probe confirmed this operator: two identity
Mat2D values compared equal and an identity/translated pair compared unequal.
if mat1 == mat2 then
print("Same transform")
end
Example:
-- Rotate around a point (translate to origin, rotate, translate back)
local toOrigin = Mat2D.withTranslation(-50, -50)
local rotate = Mat2D.withRotation(math.rad(45))
local fromOrigin = Mat2D.withTranslation(50, 50)
local combined = fromOrigin * rotate * toOrigin
See Also: Renderer.transform
Mat4
4x4 matrix type for 3D-style transforms, projection, and GPU-friendly math.
Mat4 supports:
- named fields
m11throughm44 - numeric indexing
mat[1]throughmat[16] - column-major storage order
Constructors and Static Helpers
Mat4.identity(): Mat4
Mat4.values(
c0x: number, c0y: number, c0z: number, c0w: number,
c1x: number, c1y: number, c1z: number, c1w: number,
c2x: number, c2y: number, c2z: number, c2w: number,
c3x: number, c3y: number, c3z: number, c3w: number
): Mat4
Mat4.fromTranslation(x: number, y: number, z: number): Mat4
Mat4.fromScale(x: number, y?: number, z?: number): Mat4
Mat4.fromRotationX(radians: number): Mat4
Mat4.fromRotationY(radians: number): Mat4
Mat4.fromRotationZ(radians: number): Mat4
Mat4.perspective(fovY: number, aspect: number, near: number, far: number): Mat4
Mat4.perspectiveReverseZ(fovY: number, aspect: number, near: number): Mat4
Mat4.lookAt(eye: Vector, center: Vector, up: Vector): Mat4
Mat4.ortho(left: number, right: number, bottom: number, top: number, near: number, far: number): Mat4
Mat4.multiply(out: Mat4, a: Mat4, b: Mat4): Mat4
Mat4.multiplyAffine(out: Mat4, a: Mat4, b: Mat4): Mat4
Mat4.invert(out: Mat4, input: Mat4): boolean
Mat4.invertAffine(out: Mat4, input: Mat4): boolean
Instance Methods
mat:invert(): Mat4?
mat:invertAffine(): Mat4?
mat:transpose(): Mat4
mat:transformPoint(x: number, y: number, z: number): Vector
mat:transformVec4(x: number, y: number, z: number, w: number): number, number, number, number
mat:writeToBuffer(buf: buffer, byteOffset: number)
writeToBuffer writes 64 bytes as 16 float32 values in column-major order, making it the standard bridge between Mat4 and shader uniform buffers.
Operators
Mat4 * Mat4 -> Mat4
matA == matB -> boolean
The August 14 Rive Beta MCP runtime probe confirmed Mat4 equality: two
identity matrices compared equal and an identity/translated pair compared
unequal.
Example
local model = Mat4.fromTranslation(0, 0, -4)
local spin = Mat4.fromRotationY(math.rad(30))
local composed = model * spin
local x, y, z, w = composed:transformVec4(1, 0, 0, 1)
print("vec4:", x, y, z, w)
GPU Uniform Example
local proj = Mat4.perspective(math.rad(60), 16 / 9, 0.1, 100)
local view = Mat4.fromTranslation(0, 0, -3)
local model = Mat4.fromRotationY(angle)
local mvp = proj * view * model
local bytes = buffer.create(64)
mvp:writeToBuffer(bytes, 0)
self.cameraBuffer:write(bytes, 0)
For reverse-Z depth, use Mat4.perspectiveReverseZ(...), clear depth to 0.0, and use depth compare "greater".
Mat4.lookAt creates a right-handed view matrix. Mat4.ortho creates a
right-handed orthographic projection with depth mapped to [0, 1]. These are
math helpers for scripted rendering; they do not make the general Rive scene
graph a 3D scene.
local eye = Vector.xyz(0, 0, 4)
local view = Mat4.lookAt(eye, Vector.xyz(0, 0, 0), Vector.xyz(0, 1, 0))
local projection = Mat4.ortho(-2, 2, -2, 2, 0.1, 100)
local viewProjection = projection * view
Next Steps
- Continue to Drawing
- Need a refresher? Review Quick Reference