Skip to main content

Scope

A container for scheduled work. All tasks within a Scope share the same lifecycle: pausing, resuming, or destroying the Scope affects every task registered to it.

Created via chrono.scope().

local scope = chrono.scope({ timeScale = 0.5 })

scope:tick(60, function(dt)
	player.position += player.velocity * dt
end)

scope:after(10, function()
	scope:destroy() -- cleans up all tasks at once
end)

Properties

timeScale

This item is read only and cannot be modified. Read Only
Scope.timeScale: number

The current time multiplier for this Scope. Affects how fast after, every, and tick tasks advance. Defaults to 1.0. Modify via setTimeScale().

isPaused

This item is read only and cannot be modified. Read Only
Scope.isPaused: boolean

Whether this Scope is currently paused.

isDestroyed

This item is read only and cannot be modified. Read Only
Scope.isDestroyed: boolean

Whether this Scope has been permanently destroyed. A destroyed Scope cannot be used and all its child Handles are invalid.

elapsedTime

This item is read only and cannot be modified. Read Only
Scope.elapsedTime: number

Total time elapsed in this Scope in seconds, scaled by timeScale. Does not advance while the Scope is paused.

Functions

after

Scope.after(
selfScopeInternal,
delaynumber,--

Delay in seconds. Values ≤ 0 execute on the next frame.

fn() → (),--

The function to execute.

config{
namestring?,
maxCatchupnumber?,
fireImmediatelyboolean?
}?--

Optional configuration. name labels this task in the profiler.

) → Handle

Schedules a function to execute once after delay seconds. The Handle is automatically cancelled after the function runs.

every

Scope.every(
selfScopeInternal,
intervalnumber,--

Time between executions in seconds.

fn() → (),--

The function to execute.

config{
namestring?,
maxCatchupnumber?,
fireImmediatelyboolean?
}?--

Optional configuration. fireImmediately controls whether the task fires on registration (default: true).

) → Handle

Schedules a function to execute repeatedly every interval seconds. By default, fires immediately on registration, then repeats. Set fireImmediately = false in the config to skip the first execution. Respects the Scope's timeScale.

frame

Scope.frame(
selfScopeInternal,
fn(dtnumber) → (),--

Called each frame. dt is the frame delta in seconds, or 0 when the Scope is paused.

config{
namestring?,
maxCatchupnumber?,
fireImmediatelyboolean?
}?--

Optional configuration. name labels this task in the profiler.

) → Handle

Schedules a function to execute every frame via RunService.Heartbeat. Unlike other task types, frame tasks always fire even when the Scope is paused — they receive dt = 0 instead of the real frame delta.

Tasks run in order of creation, after all tick tasks for the frame.

tick

Scope.tick(
selfScopeInternal,
hznumber,--

Target executions per second (e.g. 60 for 60 hz).

fn(dtnumber) → (),--

Called with a fixed delta of 1/hz on each tick.

config{
namestring?,
maxCatchupnumber?,
fireImmediatelyboolean?
}?--

Optional configuration. maxCatchup caps how many times the task can fire in a single frame, preventing hitches after freezes (default: unlimited).

) → Handle

Schedules a function to execute at a fixed frequency, independent of frame rate. If the frame rate drops below hz, the function fires multiple times per frame to compensate. Respects the Scope's timeScale.

Tasks run in order of creation, before all frame tasks for the frame.

pause

Scope.pause(selfScopeInternal) → ()

Pauses all tasks in this Scope. Timers do not advance while paused. frame tasks still fire each frame but receive dt = 0.

Calling pause() on an already-paused Scope is a no-op.

resume

Scope.resume(selfScopeInternal) → ()

Resumes all tasks in this Scope after a pause. No time is backfilled — timers continue from where they were when the Scope was paused.

Errors

TypeDescription
"Scope is destroyed"Thrown if the Scope was destroyed.

setTimeScale

Scope.setTimeScale(
selfScopeInternal,
scalenumber--

Time multiplier. Negative values are clamped to 0 (a warning is emitted).

) → ()

Sets the time multiplier for this Scope. Affects how fast after, every, and tick tasks advance. frame tasks always receive wall-clock dt regardless.

scale Effect
1.0 Normal speed (default)
0.5 Half speed — slow motion
2.0 Double speed — fast-forward
0 Freezes time (distinct from pause())

destroy

Scope.destroy(selfScopeInternal) → ()

Permanently destroys this Scope and cancels all its tasks. All child Handles become invalid. This operation cannot be undone.

NOTE

Also available as scope:Destroy() for compatibility with Trove and similar cleanup utilities.

Show raw api
{
    "functions": [
        {
            "name": "after",
            "desc": "Schedules a function to execute once after `delay` seconds. The Handle is\nautomatically cancelled after the function runs.",
            "params": [
                {
                    "name": "self",
                    "desc": "",
                    "lua_type": "ScopeInternal"
                },
                {
                    "name": "delay",
                    "desc": "Delay in seconds. Values ≤ 0 execute on the next frame.",
                    "lua_type": "number"
                },
                {
                    "name": "fn",
                    "desc": "The function to execute.",
                    "lua_type": "() -> ()"
                },
                {
                    "name": "config",
                    "desc": "Optional configuration. `name` labels this task in the profiler.",
                    "lua_type": "{name: string?, maxCatchup: number?, fireImmediately: boolean?}?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Handle"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 396,
                "path": "src/Chrono.luau"
            }
        },
        {
            "name": "every",
            "desc": "Schedules a function to execute repeatedly every `interval` seconds. By default,\nfires immediately on registration, then repeats. Set `fireImmediately = false` in\nthe config to skip the first execution. Respects the Scope's `timeScale`.",
            "params": [
                {
                    "name": "self",
                    "desc": "",
                    "lua_type": "ScopeInternal"
                },
                {
                    "name": "interval",
                    "desc": "Time between executions in seconds.",
                    "lua_type": "number"
                },
                {
                    "name": "fn",
                    "desc": "The function to execute.",
                    "lua_type": "() -> ()"
                },
                {
                    "name": "config",
                    "desc": "Optional configuration. `fireImmediately` controls whether the task fires on registration (default: `true`).",
                    "lua_type": "{name: string?, maxCatchup: number?, fireImmediately: boolean?}?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Handle"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 421,
                "path": "src/Chrono.luau"
            }
        },
        {
            "name": "frame",
            "desc": "Schedules a function to execute every frame via `RunService.Heartbeat`. Unlike\nother task types, `frame` tasks always fire even when the Scope is paused —\nthey receive `dt = 0` instead of the real frame delta.\n\nTasks run in order of creation, after all `tick` tasks for the frame.",
            "params": [
                {
                    "name": "self",
                    "desc": "",
                    "lua_type": "ScopeInternal"
                },
                {
                    "name": "fn",
                    "desc": "Called each frame. `dt` is the frame delta in seconds, or `0` when the Scope is paused.",
                    "lua_type": "(dt: number) -> ()"
                },
                {
                    "name": "config",
                    "desc": "Optional configuration. `name` labels this task in the profiler.",
                    "lua_type": "{name: string?, maxCatchup: number?, fireImmediately: boolean?}?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Handle"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 444,
                "path": "src/Chrono.luau"
            }
        },
        {
            "name": "tick",
            "desc": "Schedules a function to execute at a fixed frequency, independent of frame rate.\nIf the frame rate drops below `hz`, the function fires multiple times per frame\nto compensate. Respects the Scope's `timeScale`.\n\nTasks run in order of creation, before all `frame` tasks for the frame.",
            "params": [
                {
                    "name": "self",
                    "desc": "",
                    "lua_type": "ScopeInternal"
                },
                {
                    "name": "hz",
                    "desc": "Target executions per second (e.g. `60` for 60 hz).",
                    "lua_type": "number"
                },
                {
                    "name": "fn",
                    "desc": "Called with a fixed delta of `1/hz` on each tick.",
                    "lua_type": "(dt: number) -> ()"
                },
                {
                    "name": "config",
                    "desc": "Optional configuration. `maxCatchup` caps how many times the task can fire in a single frame, preventing hitches after freezes (default: unlimited).",
                    "lua_type": "{name: string?, maxCatchup: number?, fireImmediately: boolean?}?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Handle"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 463,
                "path": "src/Chrono.luau"
            }
        },
        {
            "name": "pause",
            "desc": "Pauses all tasks in this Scope. Timers do not advance while paused. `frame`\ntasks still fire each frame but receive `dt = 0`.\n\nCalling `pause()` on an already-paused Scope is a no-op.",
            "params": [
                {
                    "name": "self",
                    "desc": "",
                    "lua_type": "ScopeInternal"
                }
            ],
            "returns": [],
            "function_type": "static",
            "source": {
                "line": 481,
                "path": "src/Chrono.luau"
            }
        },
        {
            "name": "resume",
            "desc": "Resumes all tasks in this Scope after a pause. No time is backfilled — timers\ncontinue from where they were when the Scope was paused.",
            "params": [
                {
                    "name": "self",
                    "desc": "",
                    "lua_type": "ScopeInternal"
                }
            ],
            "returns": [],
            "function_type": "static",
            "errors": [
                {
                    "lua_type": "\"Scope is destroyed\"",
                    "desc": "Thrown if the Scope was destroyed."
                }
            ],
            "source": {
                "line": 494,
                "path": "src/Chrono.luau"
            }
        },
        {
            "name": "setTimeScale",
            "desc": "Sets the time multiplier for this Scope. Affects how fast `after`, `every`, and\n`tick` tasks advance. `frame` tasks always receive wall-clock `dt` regardless.\n\n| `scale` | Effect |\n| ------- | ------ |\n| `1.0`   | Normal speed (default) |\n| `0.5`   | Half speed — slow motion |\n| `2.0`   | Double speed — fast-forward |\n| `0`     | Freezes time (distinct from `pause()`) |",
            "params": [
                {
                    "name": "self",
                    "desc": "",
                    "lua_type": "ScopeInternal"
                },
                {
                    "name": "scale",
                    "desc": "Time multiplier. Negative values are clamped to `0` (a warning is emitted).",
                    "lua_type": "number"
                }
            ],
            "returns": [],
            "function_type": "static",
            "source": {
                "line": 514,
                "path": "src/Chrono.luau"
            }
        },
        {
            "name": "destroy",
            "desc": "Permanently destroys this Scope and cancels all its tasks. All child Handles\nbecome invalid. This operation cannot be undone.\n\n:::note\nAlso available as `scope:Destroy()` for compatibility with [Trove](https://sleitnick.github.io/RbxUtil/api/Trove)\nand similar cleanup utilities.\n:::",
            "params": [
                {
                    "name": "self",
                    "desc": "",
                    "lua_type": "ScopeInternal"
                }
            ],
            "returns": [],
            "function_type": "static",
            "source": {
                "line": 534,
                "path": "src/Chrono.luau"
            }
        }
    ],
    "properties": [
        {
            "name": "timeScale",
            "desc": "The current time multiplier for this Scope. Affects how fast `after`, `every`,\nand `tick` tasks advance. Defaults to `1.0`. Modify via `setTimeScale()`.",
            "lua_type": "number",
            "readonly": true,
            "source": {
                "line": 294,
                "path": "src/Chrono.luau"
            }
        },
        {
            "name": "isPaused",
            "desc": "Whether this Scope is currently paused.",
            "lua_type": "boolean",
            "readonly": true,
            "source": {
                "line": 302,
                "path": "src/Chrono.luau"
            }
        },
        {
            "name": "isDestroyed",
            "desc": "Whether this Scope has been permanently destroyed. A destroyed Scope cannot\nbe used and all its child Handles are invalid.",
            "lua_type": "boolean",
            "readonly": true,
            "source": {
                "line": 311,
                "path": "src/Chrono.luau"
            }
        },
        {
            "name": "elapsedTime",
            "desc": "Total time elapsed in this Scope in seconds, scaled by `timeScale`. Does not\nadvance while the Scope is paused.",
            "lua_type": "number",
            "readonly": true,
            "source": {
                "line": 320,
                "path": "src/Chrono.luau"
            }
        }
    ],
    "types": [],
    "name": "Scope",
    "desc": "A container for scheduled work. All tasks within a Scope share the same\nlifecycle: pausing, resuming, or destroying the Scope affects every task\nregistered to it.\n\nCreated via `chrono.scope()`.\n\n```lua\nlocal scope = chrono.scope({ timeScale = 0.5 })\n\nscope:tick(60, function(dt)\n\tplayer.position += player.velocity * dt\nend)\n\nscope:after(10, function()\n\tscope:destroy() -- cleans up all tasks at once\nend)\n```",
    "source": {
        "line": 285,
        "path": "src/Chrono.luau"
    }
}