Elysium scripting Editor API index
Build

Math

Vectors, colours, and the extra maths functions - with the angle and unit conventions the rest of the API uses.

Positions, directions, angles and colours are passed around as small tables with a few methods. They are ordinary Lua tables underneath, so { x = 1, y = 2, z = 3 } is accepted wherever a vec3 is wanted, and { 255, 0, 0 } wherever a colour is.

#vec3

A point or a direction in the world, or a set of angles. x, y and z are plain fields you can read and write.

local a = vec3( 100, 0, 0 )
local b = vec3( 100, 50, 0 )

print( a:dist( b ) )                 --> 50
print( ( a + b ) * 0.5 )             --> the point between them

#vec3( x, y, z )

Makes a vector. vec3() is the zero vector; vec3( other ) copies one.

#Operators

ExpressionResult
a + b, a - bcomponent-wise sum and difference
a * b, a / bcomponent-wise product and quotient
a * n, n * a, a / nevery component scaled by the number n
a + n, a - nn added to or subtracted from every component
-aevery component negated
a == btrue when all three components are equal

tostring( a ) gives vec3(x, y, z).

#v:unpack() → x, y, z

The three components as three values.

#v:copy() → vec3

A new vector with the same components.

#v:set( x, y, z ) / v:set( other ) → v

Changes v in place and returns it. The only method that does; everything else returns a new vector.

#v:len() → number

The length of the vector.

#v:len2d() → number

The length ignoring z - the horizontal part.

#v:len_sq() → number

The length squared. Cheaper than len() when you only compare lengths.

#v:dist( other ) → number

The distance to another point.

#v:dist2d( other ) → number

The horizontal distance.

#v:dist_sq( other ) → number

The distance squared.

#v:dot( other ) → number

#v:cross( other ) → vec3

#v:norm() → vec3

The vector scaled to length 1; the zero vector stays zero.

#v:lerp( other, t ) → vec3

The point t of the way from v to other: t = 0 is v, 1 is other, and the range is not limited.

#v:is_zero() → boolean

#v:angles() → vec3

Treating v as a direction, the pitch and yaw that look along it - angles with z (roll) 0.

#v:forward() → vec3

#v:right() → vec3

#v:up() → vec3

Treating v as a set of angles, the direction the view looks along, to its right, and upward. These are what turn "where am I looking" into a ray.

local ahead = me:eye_angles( ):forward( ) * 1000       -- a point 1000 units ahead of the view direction

#v:wrapped() → vec3

Angles brought into the legal range: yaw to -180..180, pitch limited to -89..89, roll 0.

#vec3.angle_to( from, to ) → vec3

The angles that look from the point from at the point to.

#vec2

A point on the screen, or any pair of numbers. Fields x and y. The operators + - * /, -a and == work as for vec3.

#vec2( x, y )

vec2() is 0, 0; vec2( other ) copies.

#v:unpack() → x, y

#v:len() → number

#v:dist( other ) → number

#v:dot( other ) → number

#v:norm() → vec2

#v:lerp( other, t ) → vec2

#color

A colour with four channels, each 0 to 255: r, g, b and a (alpha, 255 is opaque). Colours are immutable in practice - every method returns a new one.

#color( r, g, b [, a] )

Makes a colour. Channels outside 0..255 are clamped; alpha is 255 if left out. color() is opaque white.

#color.hex( text )

From a hex string: "ff8800", "#ff8800", or with alpha "ff8800cc".

#color.hsv( h, s, v [, a] )

From hue (degrees, any number - it wraps), saturation and value (0 to 1), and an optional alpha in 0 to 255.

#color.floats( r, g, b [, a] )

From channels given as 0 to 1, with alpha 1 if left out.

#c:unpack() → r, g, b, a

#c:copy() → color

#c == d

true when all four channels are equal.

#c:alpha( a ) → color

The same colour with a new alpha, 0 to 255.

#c:fade( f ) → color

The alpha multiplied by f (0 to 1) - the way to fade something in and out.

#c:mix( other, t ) → color

A blend, t from 0 (this colour) to 1 (the other); alpha is blended too.

#c:lighten( f ), c:darken( f ) → color

Toward white, or toward black, by f from 0 to 1.

#c:hex( [with_alpha] ) → string

"rrggbb", or "rrggbbaa" with with_alpha = true.

#c:hsv() → h, s, v, a

Hue in degrees, saturation and value from 0 to 1, and the alpha as it is.

local base  = color.hex( "#a855f7" )
local dim   = base:darken( 0.4 )
local glass = base:fade( 0.25 )

#More maths

The standard math library is all there, plus:

#math.clamp( v, lo, hi ) → number

v limited to the range.

#math.lerp( a, b, t ) → number

#math.inv_lerp( a, b, v ) → number

Where v sits between a and b, as t for lerp: the inverse of lerp. Not clamped; 0 if a == b.

#math.remap( v, from_lo, from_hi, to_lo, to_hi ) → number

Maps v from one range to another, clamped to the new range.

local alpha = math.remap( distance, 500, 2000, 255, 40 )      -- fades from 255 to 40 as it gets farther

#math.round( v [, digits] ) → number

Rounds to the nearest integer, or to digits decimals (0 to 12).

#math.sign( v ) → integer

-1, 0 or 1.

#math.smoothstep( t ) → number

An S-curve from 0 to 1 for t in 0..1.

#math.approach( current, target, step ) → number

Moves current toward target by at most step and never past it - for smooth following, with step = speed * render.dt().

#math.wrap( v, lo, hi ) → number

v brought into the range [lo, hi), wrapping around.

#math.wrap_angle( degrees ) → number

An angle brought into -180..180.

#math.angle_diff( from, to ) → number

The shortest signed turn from one angle to the other, in degrees, in -180..180.

#math.lerp_angle( a, b, t ) → number

lerp that takes the short way round a circle.

#math.fov( view_angles, target_angles ) → number

The angle in degrees between two directions given as angle sets - how far a target is from the crosshair.

#Easing curves

math.ease holds easing functions. Each takes t from 0 to 1 (values outside are clamped) and returns the eased value, 0 at the start and 1 at the end:

math.ease.linear( t )no easing
math.ease.in_quad( t ), out_quad, in_out_quadquadratic: slow start, slow end, or both
math.ease.in_cubic( t ), out_cubic, in_out_cubicthe same, steeper
math.ease.in_sine( t ), out_sine, in_out_sinegentle, sine-shaped
math.ease.out_expo( t )fast start, very soft landing
math.ease.out_back( t )overshoots a little, then settles
local started = render.time( )

on( "frame", function( canvas )
	local t = math.ease.out_cubic( ( render.time( ) - started ) / 0.4 )      -- 0.4 seconds
	canvas:fill( 20, 20, 200 * t, 10, color( 168, 85, 247 ) )
end )
Elysium scripting guide · built 2026-10-03