---
title: "engine"
description: "The engine namespace — the engine's Luau API reference for engine."
section: "API Reference"
slug: "api-engine"
canonical: "https://origozero.ai/docs/api-engine"
updated: "2026-09-05T23:13:46.387293578+00:00"
tags: ["api", "reference"]
---

# engine

The `engine` namespace — 36 functions.

## globals/engine/discardPlayChanges {#globals-engine-discardplaychanges}

```lua
engine.discardPlayChanges() -> ()
```

Arm the leave-play safeguard's deliberate discard for the play session this is called from, so that session's play to edit flip proceeds and discards its unaccepted changes.

**Returns** `()`

## globals/engine/gameplayReady {#globals-engine-gameplayready}

```lua
engine.gameplayReady -> boolean
```

Whether gameplay simulation is running: not paused, and the play scene materialized. Read-only.

**Returns** `boolean`

## globals/engine/gpuCompute {#globals-engine-gpucompute}

```lua
engine.gpuCompute -> boolean
```

Whether this process holds a live GPU device, so compute dispatch is available. Read-only.

**Returns** `boolean`

## globals/engine/headless {#globals-engine-headless}

```lua
engine.headless -> boolean
```

Whether this boot renders offscreen with no window a person can see. Content that only serves someone at a display stands down when it reads true. Read-only.

**Returns** `boolean`

## globals/engine/markScriptingBaseline {#globals-engine-markscriptingbaseline}

```lua
engine.markScriptingBaseline() -> number
```

Record the scripting registries — world-event subscriptions, the
four lifecycle-watcher lists, and the require cache — as they stand
right now, and make that the point `engine.resetScriptingState()`
restores to. Replaces any previous mark. Returns the new mark's
generation, counting from 1.
Mark once the engine is serving rather than while it boots: the
registries keep growing as the prelude subscribes, the world
entrypoint runs and the startup scene loads, so a mark taken partway
through sits below the rest of that work and the first reset would
remove it.

**Returns** `number`

```lua
engine.markScriptingBaseline()
world.on("player_join", function() end)
engine.resetScriptingState() -- the subscription above is gone
```

## globals/engine/mode {#globals-engine-mode}

```lua
engine.mode -> "edit" | "play"
```

The engine mode this process is in, `edit` or `play`. Assigning it takes the flip, side effects and all.

**Returns** `"edit" | "play"`

## globals/engine/offDeviceRebuilt {#globals-engine-offdevicerebuilt}

```lua
engine.offDeviceRebuilt(id: number) -> boolean
```

Remove an `engine.onDeviceRebuilt` subscriber by its watcher id.
Returns true when a live watcher carried that id, false when it named
none — already removed, or never registered.

**Parameters**

- `id` `number` — Watcher id returned by `engine.onDeviceRebuilt`.

**Returns** `boolean`

```lua
local id = engine.onDeviceRebuilt(function() end)
engine.offDeviceRebuilt(id)
```

## globals/engine/offModeChange {#globals-engine-offmodechange}

```lua
engine.offModeChange(id: number) -> boolean
```

Remove an `engine.onModeChange` subscriber by its watcher id.
Returns true when a live watcher carried that id, false when it
named none — already removed, or never registered.

**Parameters**

- `id` `number` — Watcher id returned by `engine.onModeChange`.

**Returns** `boolean`

```lua
local id = engine.onModeChange(function() end)
engine.offModeChange(id)
```

## globals/engine/offPauseChange {#globals-engine-offpausechange}

```lua
engine.offPauseChange(id: number) -> boolean
```

Remove an `engine.onPauseChange` subscriber by its watcher id.
Returns true when a live watcher carried that id, false when it
named none.

**Parameters**

- `id` `number` — Watcher id returned by `engine.onPauseChange`.

**Returns** `boolean`

## globals/engine/offWorldLoaded {#globals-engine-offworldloaded}

```lua
engine.offWorldLoaded(id: number) -> boolean
```

Remove an `onWorldLoaded` subscriber by its watcher id.

**Parameters**

- `id` `number`

**Returns** `boolean`

## globals/engine/offWorldReady {#globals-engine-offworldready}

```lua
engine.offWorldReady(id: number) -> boolean
```

Remove an `engine.onWorldReady` subscriber by its watcher id.
Returns true when a live watcher carried that id, false when it
named none.

**Parameters**

- `id` `number` — Watcher id returned by `engine.onWorldReady`.

**Returns** `boolean`

## globals/engine/offWorldUnloading {#globals-engine-offworldunloading}

```lua
engine.offWorldUnloading(id: number) -> boolean
```

Remove an `engine.onWorldUnloading` subscriber by its watcher
id. Returns true when a live watcher carried that id, false when it
named none.

**Parameters**

- `id` `number` — Watcher id returned by `engine.onWorldUnloading`.

**Returns** `boolean`

## globals/engine/onDeviceRebuilt {#globals-engine-ondevicerebuilt}

```lua
engine.onDeviceRebuilt(callback: (number) -> ()) -> number
```

Register a callback that fires after the engine has answered a lost
render device by building another one. The callback receives the new
device generation — a number that counts the devices this session has run
on, and moves exactly once per rebuild. Returns a watcher id.

A device is lost when the driver resets, when the GPU is taken away, or
when a browser reclaims a WebGPU context. Everything the engine can
re-derive by itself it does: meshes, materials, shaders, render passes and
the UI are all back on the new device before this fires. What it cannot
re-derive is what YOUR content made and only the GPU held — a texture
uploaded from pixels a script computed, a compute buffer it filled, a
render target it created. Make those again here.

Content that owns no GPU resource of its own needs no subscriber: asset
handles re-materialise on their next use.

**Parameters**

- `callback` `(number) -> ()` — Function invoked as `(generation: number)`.

**Returns** `number`

```lua
engine.onDeviceRebuilt(function(generation)
-- the noise field lived only on the GPU, so it is computed again
regenerateNoiseTexture()
end)
```

## globals/engine/onModeChange {#globals-engine-onmodechange}

```lua
engine.onModeChange(callback: (string, string) -> ()) -> number
```

Register a callback that fires synchronously whenever
`engine.mode` changes. Callback receives `(newMode, oldMode)` as
strings. Returns a watcher id for future removal. Consumers
(player_spawner, camera_spawner, editor-UI bootstrap, world
entrypoint top-level `onModeChange`, etc.) all subscribe through
this single API — there is no other fire path. Mode is engine
state, so the watcher hangs off the `engine` module.

**Parameters**

- `callback` `(string, string) -> ()` — Function invoked as `(newMode: string, oldMode: string)`.

**Returns** `number`

```lua
local id = engine.onModeChange(function(new, old)
print("flipped " .. old .. " -> " .. new)
end)
```

## globals/engine/onPauseChange {#globals-engine-onpausechange}

```lua
engine.onPauseChange(callback: (boolean, boolean) -> ()) -> number
```

Register a callback that fires synchronously whenever the gameplay
pause flag flips via an explicit `engine.paused` write. Callback
receives `(newPaused, oldPaused)` as booleans. Returns a watcher id.
Pause is independent of `engine.mode`: pausing play mode returns the
editor authoring surface (free camera + EditorOnly entities) over the
frozen play world, and resuming hides it again. Mode-driven pause
resets (the edit=paused / play=running defaults applied on a mode flip)
are delivered through `onModeChange`, not this hook.

**Parameters**

- `callback` `(boolean, boolean) -> ()` — Function invoked as `(newPaused: boolean, oldPaused: boolean)`.

**Returns** `number`

```lua
local id = engine.onPauseChange(function(paused)
print(paused and "frozen" or "running")
end)
```

## globals/engine/onWorldLoaded {#globals-engine-onworldloaded}

```lua
engine.onWorldLoaded(callback: () -> ()) -> number
```

Register a callback fired (no args) when the world is fully
LOADED — its `.world_entrypoint.luau` ran AND its `onWorldLoad`
returned (the startup scene loaded, defaults seeded, editor UI
mounted). This is strictly AFTER `onWorldReady` (content synced):
ready = "bytes are in the VFS"; loaded = "the entrypoint has run".
LATCHED — a callback registered after the world is already loaded
fires immediately, so a late consumer never misses it and never has
to poll. Read the same state synchronously via `engine.worldLoaded`.

**Parameters**

- `callback` `() -> ()` — Function invoked with no arguments.

**Returns** `number`

## globals/engine/onWorldReady {#globals-engine-onworldready}

```lua
engine.onWorldReady(callback: () -> ()) -> number
```

Register a callback fired (no args) when the bound world's
content has been synced into the VFS and the world is ready to
load. This is the race-free, user-space hook that drives the whole
world-VM lifecycle: the builtin world-entrypoint loader subscribes
to it and, when it fires, `loadstring(vfs.read(...))`s
`/source/.world_entrypoint.luau` and runs its `onWorldLoad` —
exactly the way a scene entrypoint loads. The trusted VM fires this
(via `world.markReady()`) ONLY once the bytes are in the VFS, so a
subscriber never sees a half-synced world. Returns a watcher id.

**Parameters**

- `callback` `() -> ()` — Function invoked with no arguments.

**Returns** `number`

## globals/engine/onWorldUnloading {#globals-engine-onworldunloading}

```lua
engine.onWorldUnloading(callback: () -> ()) -> number
```

Symmetric teardown of `engine.onWorldReady`: register a callback
fired (no args) when the bound world is unbinding/swapping out. The
builtin loader runs the world entrypoint's `onWorldUnload` here, so
the world entrypoint has the same load/unload parity a scene
entrypoint has. Returns a watcher id.

**Parameters**

- `callback` `() -> ()` — Function invoked with no arguments.

**Returns** `number`

## globals/engine/paused {#globals-engine-paused}

```lua
engine.paused -> boolean
```

Whether gameplay is paused: `update(dt)` component callbacks are gated off while `editorUpdate(dt)` keeps firing in edit mode.

**Returns** `boolean`

## globals/engine/profile {#globals-engine-profile}

```lua
engine.profile -> "editor" | "runtime"
```

The boot profile this process started under, `editor` or `runtime`. Read-only.

**Returns** `"editor" | "runtime"`

## globals/engine/resetScriptingState {#globals-engine-resetscriptingstate}

```lua
engine.resetScriptingState() -> { [string]: number }
```

Drop every world-event subscription, lifecycle watcher and
cached module registered since the last
`engine.markScriptingBaseline()`, leaving everything registered
before it in place — including the builtin world-entrypoint loader,
which subscribes at VM boot and so always sits below any mark.
Raises when no mark has been taken. Returns per-registry counts of
what was removed: `worldEvents`, `modeWatchers`,
`worldReadyWatchers`, `worldUnloadingWatchers`, `pauseWatchers`,
`modules`, and `total`.

**Returns** `{ [string]: number }`

## globals/engine/scriptingRegistryCounts {#globals-engine-scriptingregistrycounts}

```lua
engine.scriptingRegistryCounts() -> { [string]: number }
```

How many subscriptions each scripting registry holds right now,
plus the size of the require cache and the generation of the mark in
force. Keys: `worldEvents`, `modeWatchers`, `worldReadyWatchers`,
`worldUnloadingWatchers`, `pauseWatchers`, `modules`, and
`baselineGeneration` (nil when no mark has been taken).

**Returns** `{ [string]: number }`

## globals/engine/setMode {#globals-engine-setmode}

```lua
engine.setMode(mode: string, options: { strict: boolean? }?) -> { mode: string, bypassed: { any } }
```

Change the engine mode with per-call control over the play gate, and
read back what the change went past. `engine.mode = value` is the same
flip with the defaults.

`options.strict = false` lets THIS call enter play while your own content
carries error-severity diagnostics. It settles with the call: the world's
`lsp.strict_mode` is untouched, so no other session and no later session
of the world sees a different gate. The returned `bypassed` array holds
the diagnostics the call went past — each `{ path, line, col, code,
message, severity }` — and the engine log carries the same list. An
error in content another session wrote never gates the flip, so it never
appears here; a push still refuses to publish while any of them stands.

**Parameters**

- `mode` `string` — `"edit"` or `"play"`.
- `options` `{ strict: boolean? }` _(optional)_ — `{ strict: boolean? }`. `strict = false` waives the play gate
for this call; `true` or omitted honours the world's `lsp.strict_mode`.

**Returns** `{ mode: string, bypassed: { any } }` — `{ mode, bypassed }` — the mode now in force and the diagnostics this call entered play past (empty when it went past none).

```lua
local report = engine.setMode("play", { strict = false })
for _, d in ipairs(report.bypassed) do
print(("entered play past %s:%d — %s"):format(d.path, d.line, d.message))
end
```

## globals/engine/timeScale {#globals-engine-timescale}

```lua
engine.timeScale -> number
```

The global time scale applied to the fixed-timestep accumulator and to `update(dt)`: 1.0 is real time, 0.0 frozen, 2.0 double speed.

**Returns** `number`

## globals/engine/vertexStride {#globals-engine-vertexstride}

```lua
engine.vertexStride -> number
```

Byte stride of the engine's standard GPU Vertex layout, which a mesh built from a compute buffer sizes and strides its writes to. Read-only.

**Returns** `number`

## globals/engine/worldLoaded {#globals-engine-worldloaded}

```lua
engine.worldLoaded -> boolean
```

Whether the world entrypoint's `onWorldLoad` has run to completion. Read-only.

**Returns** `boolean`

## typed/builtin//modules/api/engine/engine/engine/discardPlayChanges {#typed-builtin-modules-api-engine-engine-engine-discardplaychanges}

```lua
engine.discardPlayChanges() -> ()
```

Arm the leave-play safeguard's deliberate discard for the play session this is called from, so that session's play to edit flip proceeds and discards its unaccepted changes.

**Returns** `()`

## typed/builtin//modules/api/engine/engine/engine/gameplayReady {#typed-builtin-modules-api-engine-engine-engine-gameplayready}

```lua
engine.gameplayReady -> boolean
```

Whether gameplay simulation is running: not paused, and the play scene materialized. Read-only.

**Returns** `boolean`

## typed/builtin//modules/api/engine/engine/engine/gpuCompute {#typed-builtin-modules-api-engine-engine-engine-gpucompute}

```lua
engine.gpuCompute -> boolean
```

Whether this process holds a live GPU device, so compute dispatch is available. Read-only.

**Returns** `boolean`

## typed/builtin//modules/api/engine/engine/engine/headless {#typed-builtin-modules-api-engine-engine-engine-headless}

```lua
engine.headless -> boolean
```

Whether this boot renders offscreen with no window a person can see. Content that only serves someone at a display stands down when it reads true. Read-only.

**Returns** `boolean`

## typed/builtin//modules/api/engine/engine/engine/mode {#typed-builtin-modules-api-engine-engine-engine-mode}

```lua
engine.mode -> "edit" | "play"
```

The engine mode this process is in, `edit` or `play`. Assigning it takes the flip, side effects and all.

**Returns** `"edit" | "play"`

## typed/builtin//modules/api/engine/engine/engine/paused {#typed-builtin-modules-api-engine-engine-engine-paused}

```lua
engine.paused -> boolean
```

Whether gameplay is paused: `update(dt)` component callbacks are gated off while `editorUpdate(dt)` keeps firing in edit mode.

**Returns** `boolean`

## typed/builtin//modules/api/engine/engine/engine/profile {#typed-builtin-modules-api-engine-engine-engine-profile}

```lua
engine.profile -> "editor" | "runtime"
```

The boot profile this process started under, `editor` or `runtime`. Read-only.

**Returns** `"editor" | "runtime"`

## typed/builtin//modules/api/engine/engine/engine/timeScale {#typed-builtin-modules-api-engine-engine-engine-timescale}

```lua
engine.timeScale -> number
```

The global time scale applied to the fixed-timestep accumulator and to `update(dt)`: 1.0 is real time, 0.0 frozen, 2.0 double speed.

**Returns** `number`

## typed/builtin//modules/api/engine/engine/engine/vertexStride {#typed-builtin-modules-api-engine-engine-engine-vertexstride}

```lua
engine.vertexStride -> number
```

Byte stride of the engine's standard GPU Vertex layout, which a mesh built from a compute buffer sizes and strides its writes to. Read-only.

**Returns** `number`

## typed/builtin//modules/api/engine/engine/engine/worldLoaded {#typed-builtin-modules-api-engine-engine-engine-worldloaded}

```lua
engine.worldLoaded -> boolean
```

Whether the world entrypoint's `onWorldLoad` has run to completion. Read-only.

**Returns** `boolean`
