Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit3bb1da2647

README.md

colorSequence

Roblox-shaped RGB keyframe-curve value type. A ColorSequence is an immutable curve of up to 64 keypoints, each carrying (time, value, envelope) where value is a {r, g, b} triple and envelope is a per-channel random range half-width. The per-channel envelope is a notable improvement over Roblox, where ColorSequence ships with no envelope at all — a long-standing community wishlist item.

Exposed as the ColorSequence Luau global via --!global. Authored scripts call ColorSequence.new(...) directly without a require. The Rust FFI lives at __sequences.* (registered by crates/zero_scripting/src/ffi/bindings/curves.rs); this module is the typed Luau wrapper.

Exports

  • ColorSequence.new(...) -> ColorSequenceObj:

    • ColorSequence.new({r, g, b}) — constant color (accepts a 3-array or {r=, g=, b=} record).
    • ColorSequence.new(c0, c1) — two-point lerp from c0 to c1.
    • ColorSequence.new({ keypoint, ... }) — explicit keypoints. envelope may be omitted (= {0,0,0}), a single number (broadcast to all channels), or a 3-array. First key must anchor at time = 0, last at time = 1. NaN / Inf rejected.
  • ColorSequence.deserialize(payload) -> ColorSequenceObj — rebuild from a { kind = "ColorSequence", keypoints = {...} } payload produced by :serialize().

Keypoint shapes

An entry of a keypoint list takes any of three written shapes, and the shapes mix within one list:

WrittenRead as
{ time = 0.5, value = {1,0,0}, envelope = 0.05 }the named record
{ 0.5, {1,0,0}, 0.05 }time, then colour, then envelope
{ 1, 0, 0 }a bare colour, timed by its place in the list

A bare colour takes its time from its place: the entries spread evenly across [0, 1], so { {1,0.85,0.35}, {1,0.35,0.05} } is a ramp from the first colour at t = 0 to the second at t = 1, and a list of one colour holds that colour across the whole domain.

Methods (called via :)

  • seq:evaluate(t) -> (r, g, b) — deterministic linear interpolation at t (multiret). t clamps to [0, 1]; NaN coerces to 0.
  • seq:sample(t) -> (r, g, b) — evaluate(t) plus per-channel (math.random() − 0.5)·2·envelope(t) jitter. Per-VM math.randomseed controls jitter reproducibility.
  • seq:keypoints() -> { { time, value = {r,g,b}, envelope = {r,g,b} } } — array snapshot, sorted ascending by time.
  • seq:duration() -> number — always 1.0 for a well-formed sequence.
  • seq:serialize() -> { kind, keypoints } — scene-save payload.
  • seq:destroy() — drop the FFI handle. Luau removed __gc on tables, so eager cleanup is the caller's responsibility for per-frame-rebuild patterns; otherwise the entry dies with the VM.

Substrate

Backed by zero_curves::Channel (Linear interp). Each sequence builds the value and envelope channels once at construct time and reaches into them on every sample — zero per-call allocation, safe for hot particle loops. Validation errors name the specific rule ("first keypoint must anchor at time = 0", "too many keypoints (max 64)", …).

Foundation for VFX property-over-lifetime: declarative particles (#2572), GPU emitter (#873), beams, trails. Issue: #2673.