Skip to main content

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 Vec2D which is an alias for Vector.

Constructors

Vector.xy(x, y)

Creates a new 2D vector.

Vector.xy(x: number, y: number): Vector

Parameters:

ParameterTypeDescription
xnumberX component
ynumberY 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:

ParameterTypeDescription
fromVectorStart vector (t=0)
toVectorTarget vector (t=1)
tnumberInterpolation factor (0-1)

Example:

local midpoint = Vector.lerp(Vector.xy(0, 0), Vector.xy(100, 100), 0.5)
-- Vector(50, 50)

Deprecated Instance Methods

Deprecated

The following instance methods still work but are deprecated. Use the static Vector.* versions above for better performance.

DeprecatedUse 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

OperatorDescriptionExample
+Additionvec1 + vec2
-Subtractionvec1 - vec2
*Scalar multiplicationvec * 2
/Scalar divisionvec / 2
- (unary)Negation-vec
==Equalityvec1 == 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)

See Also: Mat2D, Color


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:

ParameterTypeDescription
rnumberRed (0-255)
gnumberGreen (0-255)
bnumberBlue (0-255)
anumberAlpha (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) not c.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:

ParameterTypeDescription
fromColorStart color (t=0)
toColorEnd color (t=1)
tnumberInterpolation 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 m11 through m44
  • numeric indexing mat[1] through mat[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