Skip to Content

Vectors and Colors

Vectors and colors are a built-in family of seven types: vec2 / vec3 / vec4 (float components), vec2i / vec3i / vec4i (int components), and color (four float components, rgba, by convention in the 0..1 range and not clamped at runtime).

Construction

Construct by calling; new is not available for vectors and colors:

val v = vec2(1.0, 2.0); print(v); print(vec2(1.0)); print(vec2i(v)); val c = color("#ff8000"); print(c); print(color(1.0, 0.5, 0.25)); print(color(0.25));
vec2(1.0, 2.0) vec2(1.0, 1.0) vec2i(1, 2) color(1.0, 0.502, 0.0, 1.0) color(1.0, 0.5, 0.25, 1.0) color(0.25, 0.25, 0.25, 0.25)
  • A single numeric argument broadcasts (vec2(1.0) gives (1.0, 1.0)); a single vector argument converts between same-width family members (float to int truncates toward zero).
  • color takes three arguments (alpha defaults to 1.0) or four; it also takes a hex string (#RGB / #RGBA / #RRGGBB / #RRGGBBAA).
  • color component arguments must be float; int is rejected. For 8-bit values, write a hex string.

Components and swizzle

Two-alphabet discipline: vec components are xyzw, color components are rgba, and the two alphabets must not be mixed. A swizzle takes 1 to 4 letters, and the result is always interpreted by the xyzw alphabet (c.rgb.y fetches the green component):

print(c.rgb); print(c.rgb.y); print(v[1]); val (x, y) = vec2i(3, 4); print(x, y);
vec3(1.0, 0.502, 0.0) 0.5019608 2 3 4

Numeric indexing v[1] is the assertion form; out of bounds is a hard error on the spot. Get(i) is the corresponding fallible form: out of bounds fails with IndexOutOfRange; after try? consumes it, out of bounds gives null and a hit gives the component value (int in the vec2i family, float in the vec family and color):

print(try? v.Get(1), try? v.Get(9)); print(try? c.Get(3) ?? 0.0);
2 null 1

Components are read-only: v.x = 1.0 and write-side swizzles are rejected at compile time. The only way to change a value is the non-destructive update (see the with section of struct):

val moved = v with { x: 9.0 }; print(moved);
vec2(9.0, 2.0)

Arithmetic

+ - * / work component-wise (between same types). A scalar may join multiplication and division on either side; with the scalar on the left, - and / run component-wise in reverse: 1.0 / vec2(2.0, 4.0) gives vec2(0.5, 0.25). == / != compare by value. Ordering comparisons, bitwise operations, and exponentiation are not available.

Method surface

Method dispatch is built in and does not depend on imports:

MemberApplies toDescription
Lengthvec familymagnitude, property form (no parentheses)
Dot / Distancevec familydot product / distance between two points
Normalizedvec familynormalize to unit length
Crossvec3 onlycross product
Lerpvec familyinterpolate by a coefficient
Hexcolor onlyto a hex string (alpha included, #ff8000ff)
ToHsv / FromHsvcolor / vec3hsv channels by convention 0..1, clamped when out of range
print(v.Length); print(vec2(1.0, 0.0).Dot(vec2(0.0, 1.0))); print(vec2(1.0, 2.0).Lerp(vec2(3.0, 4.0), 0.5)); print(c.Hex());
2.23606797749979 0 vec2(2.0, 3.0) #ff8000ff

Compile-time folding

Construction from pure literals, unary negation, the four arithmetic operations, and swizzle chains fold into constants at compile time. The const position supports this too: const V = vec2(1.0, 2.0); is legal, and V.x is 1 (see const funcs). A color’s single string argument goes through compile-time hex parsing in a const position.

Notes

  • color’s 0..1 is a convention, not clamped at runtime: color(2.0, 0.5, 0.25) is legal, and what falls outside is interpreted by the renderer.
  • match constructor pattern arms support vectors (vec2(1.0, 0.0) => ...); see Patterns.
Last updated on October 11, 2026