Skip to content

Clock ​

Shared clock for timers, frame callbacks, and deterministic stepping in tests. Delegates to os.clock(), workspace:GetServerTimeNow(), task.*, and RunService in live games, or steps virtual time in manual mode (Clock.manual(true)).

luau
local Clock = Flux.Clock
Clock.manual(true)
Clock.delay(1, function() print("one second later") end)
Clock.step(1) -- advances 1 second in 1/60s frames

Types ​

Phase type ​

luau
type Phase = "PreRender" | "RenderStepped" | "Heartbeat"

Functions ​

now ​

luau
Clock.now(): number

Seconds on a monotonic clock, for measuring intervals: os.clock() in a game, virtual time on a manual clock.

wall ​

luau
Clock.wall(): number

Unix time in seconds, for dates and countdowns shared between players: workspace:GetServerTimeNow() in a game, 2026-01-01T00:00:00Z + now() on a manual clock.

delay ​

luau
Clock.delay<A...>(seconds: number?, fn: ((A...) -> ...any) | thread, ...: A...): thread

Runs fn(...) (or resumes a thread) after seconds, like task.delay. Returns the thread, for cancel.

wait Yields ​

luau
Clock.wait(seconds: number?): number

Yields the running thread for seconds (default: one frame), like task.wait, and returns the time it waited. On a manual clock it waits for a step to pass the deadline, so it needs a coroutine to yield.

defer ​

luau
Clock.defer<A...>(fn: ((A...) -> ...any) | thread, ...: A...): thread

Runs fn(...) (or resumes a thread) once the current work finishes, like task.defer. On a manual clock it waits for drain() (which every step frame calls). Returns the thread, for cancel.

cancel ​

luau
Clock.cancel(thread: thread)

Cancels a thread from delay, defer or a wait, like task.cancel.

connect ​

luau
Clock.connect(phase: Phase, fn: (dt: number) -> ()): () -> ()

Calls fn(dt) every frame at phase ("PreRender", "RenderStepped" or "Heartbeat"). Returns a function that disconnects it. A manual clock calls it from step instead of the engine's frames.

manual ​

luau
Clock.manual(on: boolean)

Toggles manual clock mode for tests (headless Luau is always manual). Disabling manual mode clears pending tasks via Clock.reset().

isManual ​

luau
Clock.isManual(): boolean

Tests only: whether the clock is manual.

drain ​

luau
Clock.drain()

Tests only: runs deferred work until none is left, including work it defers in turn. Errors after 10000 calls, when deferred work keeps deferring more.

step ​

luau
Clock.step(seconds: number, frameLength: number?)

Tests only: advances a manual clock by seconds, in frames of at most frameLength seconds (default 1/60). step(0) runs one frame of zero length: due timers, subscribers, a Flux step and the deferred work.

reset ​

luau
Clock.reset()

Tests only: drops every pending timer, wait, deferred call and manual subscriber. now keeps its value.