Skip to main content

Advanced Features

This guide covers advanced scene features including skyboxes, spawn points, movement bounds, gamemodes, autosave, trigger volumes, and teleport volumes.

Skybox​

Skyboxes provide environment mapping for the scene background.

"skybox": {
"uri": "https://example.com/skybox.jpg",
"rotation": {"x": 0.0, "y": 0.0, "z": 0.0}
}

Properties — specify exactly one of:

  • uri (string): equirectangular sky — LDR (.png / .jpg / .jpeg / .tga) or Radiance .hdr (HTTPS, or Allow HTTP for travel testing)
  • faces (object): six-face cubemap with px, nx, py, ny, pz, nz HTTPS URIs (all LDR or all .hdr)
  • rotation (vec3, optional): Rotation in degrees

EXR is not supported.

Use cases: Sky, space, indoor environments, atmosphere

Spawn Points​

Spawn points define where players appear when entering the scene.

"spawn": {"x": 0.0, "y": 1.0, "z": 0.0}

Default: (0, 0, 0) if not specified

Use cases:

  • Start locations
  • Respawn points
  • Scene entry points

Movement Bounds​

Movement bounds limit player movement to a specific area.

"movementBounds": {
"min": {"x": -50.0, "y": 0.0, "z": -50.0},
"max": {"x": 50.0, "y": 100.0, "z": 50.0}
}

Properties:

  • min (vec3, required): Minimum bounds (X, Y, Z)
  • max (vec3, required): Maximum bounds (X, Y, Z)

Default when omitted: movement is still clamped to approximately ±100 on each axis (min = (-100, -100, -100), max = (100, 100, 100)). Large open worlds must set explicit movementBounds or players hit an invisible wall at ±100.

Use cases:

  • Confined spaces
  • Level boundaries
  • Preventing players from leaving the play area

Gamemode Scripts​

Gamemode scripts provide scene-wide logic and state management.

"gamemode": {
"file": "gamemode-script"
}

Properties:

  • file (string, required): Asset ID of the gamemode script

Features:

  • Scene-wide state management
  • Event system
  • Save/load support
  • Persistent data via localStorage

See Lua/Luau API - Gamemode for details.

Autosave Volumes​

Autosave volumes automatically save game state when the player enters.

{
"id": "autosave-zone",
"type": "autosaveVolume",
"position": {"x": 0.0, "y": 0.0, "z": 0.0},
"rotation": {"x": 0.0, "y": 0.0, "z": 0.0},
"scale": {"x": 5.0, "y": 5.0, "z": 5.0},
"autosaveVolume": {}
}

Behavior:

  • Triggers when player enters the volume
  • Saves gamemode state and script state
  • Can show notification (configured via autosaveNotification)

Use cases: Checkpoints, safe zones, progress saving

Autosave Notifications​

Configure on-screen notifications when autosave occurs.

"autosaveNotification": {
"text": "Game saved!",
"x": 0.02,
"y": 0.95,
"font": "default-font",
"color": {"x": 1.0, "y": 1.0, "z": 1.0}
}

Properties:

  • text (string, optional): Notification text
  • x / y (number, optional): Screen position (0–1 normalized)
    • X: 0 = left, 1 = right
    • Y: 0 = bottom, 1 = top
  • font (string, optional): Font asset ID
  • color (vec3, optional): Text color (RGB, 0-1)

Trigger Volumes​

Trigger volumes detect player presence and fire author-chosen gamemode event names via Gamemode.onEvent(eventName, data). Most volume types require an eventName string in their typed properties object (not a synthetic volume_* name).

Look At Volume (type: "lookAtVolume")​

Fires when the player looks toward the volume (outside it).

{
"id": "look-trigger",
"type": "lookAtVolume",
"position": {"x": 0.0, "y": 0.0, "z": 0.0},
"rotation": {"x": 0.0, "y": 0.0, "z": 0.0},
"scale": {"x": 2.0, "y": 2.0, "z": 2.0},
"lookAtVolume": {
"eventName": "looked_at_sign",
"singleUse": false,
"cooldownSeconds": 0,
"fovDegrees": 30
}
}

Properties: eventName (required); optional singleUse, cooldownSeconds, fovDegrees

Looked At Volume (type: "lookedAtVolume")​

Fires when the player is inside the volume and looks at a target instance.

{
"id": "looked-trigger",
"type": "lookedAtVolume",
"position": {"x": 0.0, "y": 0.0, "z": 0.0},
"rotation": {"x": 0.0, "y": 0.0, "z": 0.0},
"scale": {"x": 2.0, "y": 2.0, "z": 2.0},
"lookedAtVolume": {
"eventName": "looked_at_npc",
"targetInstanceId": "npc-1",
"fovDegrees": 30
}
}

Properties: eventName, targetInstanceId (required); optional singleUse, cooldownSeconds, fovDegrees

Single Trigger Volume (type: "singleTriggerVolume")​

Fires once on enter.

{
"id": "single-trigger",
"type": "singleTriggerVolume",
"position": {"x": 0.0, "y": 0.0, "z": 0.0},
"rotation": {"x": 0.0, "y": 0.0, "z": 0.0},
"scale": {"x": 2.0, "y": 2.0, "z": 2.0},
"singleTriggerVolume": {
"eventName": "checkpoint_reached"
}
}

Multi Trigger Volume (type: "multiTriggerVolume")​

Fires every enter (optionally capped).

{
"id": "multi-trigger",
"type": "multiTriggerVolume",
"position": {"x": 0.0, "y": 0.0, "z": 0.0},
"rotation": {"x": 0.0, "y": 0.0, "z": 0.0},
"scale": {"x": 2.0, "y": 2.0, "z": 2.0},
"multiTriggerVolume": {
"eventName": "zone_entered",
"maxFires": 0,
"autoRemoveOnFire": false
}
}

Cooldown Trigger Volume (type: "cooldownTriggerVolume")​

Fires on enter with a cooldown between fires.

{
"id": "cooldown-trigger",
"type": "cooldownTriggerVolume",
"position": {"x": 0.0, "y": 0.0, "z": 0.0},
"rotation": {"x": 0.0, "y": 0.0, "z": 0.0},
"scale": {"x": 2.0, "y": 2.0, "z": 2.0},
"cooldownTriggerVolume": {
"eventName": "pickup_pulse",
"cooldownSeconds": 5.0,
"maxFires": 0
}
}

Properties: eventName, cooldownSeconds (required); optional maxFires

Exit Trigger Volume (type: "exitTriggerVolume")​

Fires when the player exits the volume.

{
"id": "exit-trigger",
"type": "exitTriggerVolume",
"position": {"x": 0.0, "y": 0.0, "z": 0.0},
"rotation": {"x": 0.0, "y": 0.0, "z": 0.0},
"scale": {"x": 2.0, "y": 2.0, "z": 2.0},
"exitTriggerVolume": {
"eventName": "left_safe_zone"
}
}

Stay Trigger Volume (type: "stayTriggerVolume")​

Fires repeatedly while the player remains inside.

{
"id": "stay-trigger",
"type": "stayTriggerVolume",
"position": {"x": 0.0, "y": 0.0, "z": 0.0},
"rotation": {"x": 0.0, "y": 0.0, "z": 0.0},
"scale": {"x": 2.0, "y": 2.0, "z": 2.0},
"stayTriggerVolume": {
"eventName": "healing_tick",
"stayInterval": 1.0
}
}

Properties: eventName, stayInterval (required)

Timed Entry Trigger Volume (type: "timedEntryTriggerVolume")​

Fires after the player has stayed inside for a duration.

{
"id": "timed-trigger",
"type": "timedEntryTriggerVolume",
"position": {"x": 0.0, "y": 0.0, "z": 0.0},
"rotation": {"x": 0.0, "y": 0.0, "z": 0.0},
"scale": {"x": 2.0, "y": 2.0, "z": 2.0},
"timedEntryTriggerVolume": {
"eventName": "channel_complete",
"requiredStayTime": 3.0
}
}

Properties: eventName, requiredStayTime (required)

Counter Trigger Volume (type: "counterTriggerVolume")​

Shares a named counter (counter_word) across volumes; increments on enter. Schema fields are counter_word / required_count only (no author eventName today).

{
"id": "counter-trigger",
"type": "counterTriggerVolume",
"position": {"x": 0.0, "y": 0.0, "z": 0.0},
"rotation": {"x": 0.0, "y": 0.0, "z": 0.0},
"scale": {"x": 2.0, "y": 2.0, "z": 2.0},
"counterTriggerVolume": {
"counter_word": "switches",
"required_count": 5,
"auto_reset_after_fire": false,
"fire_once_when_reached": false
}
}

Properties: counter_word, required_count (required); optional auto_reset_after_fire, fire_once_when_reached

Sequence Trigger Volume (type: "sequenceTriggerVolume")​

Volumes in the same sequence_group_id must be entered in ascending sequence_index order. Optional eventName is the completion event (fires once when the highest index in the group is completed correctly).

{
"id": "sequence-step-0",
"type": "sequenceTriggerVolume",
"position": {"x": 0.0, "y": 0.0, "z": 0.0},
"rotation": {"x": 0.0, "y": 0.0, "z": 0.0},
"scale": {"x": 2.0, "y": 2.0, "z": 2.0},
"sequenceTriggerVolume": {
"sequence_group_id": "puzzle_a",
"sequence_index": 0,
"reset_if_wrong": true,
"eventName": "puzzle_a_complete"
}
}

Properties:

  • sequence_group_id (string, required)
  • sequence_index (integer, required, 0-based)
  • reset_if_wrong (boolean, optional)
  • eventName (string, optional): Completion gamemode event

Toggle Trigger Volume (type: "toggleTriggerVolume")​

Toggles on/off each enter; fires on_activate_event / on_deactivate_event when set.

{
"id": "toggle-trigger",
"type": "toggleTriggerVolume",
"position": {"x": 0.0, "y": 0.0, "z": 0.0},
"rotation": {"x": 0.0, "y": 0.0, "z": 0.0},
"scale": {"x": 2.0, "y": 2.0, "z": 2.0},
"toggleTriggerVolume": {
"is_on": false,
"on_activate_event": "gate_open",
"on_deactivate_event": "gate_close"
}
}

Teleport Volumes​

Teleport volumes instantly move the player to a new position. On activation they also fire the fixed gamemode event teleport_volume_activated.

{
"id": "teleporter",
"type": "teleportVolume",
"position": {"x": 0.0, "y": 0.0, "z": 0.0},
"rotation": {"x": 0.0, "y": 0.0, "z": 0.0},
"scale": {"x": 2.0, "y": 2.0, "z": 2.0},
"teleportVolume": {
"destination": {"x": 10.0, "y": 5.0, "z": 10.0}
}
}

Properties:

  • destination (vec3, required): Target position

Use cases: Elevators, fast travel, level transitions

Interaction Volumes​

Interaction volumes fire when the player is inside and presses a bound action. The action field is matched case-insensitively against runtime action names (interact, jump, etc.).

{
"id": "interaction-zone",
"type": "interactionVolume",
"position": {"x": 0.0, "y": 0.0, "z": 0.0},
"rotation": {"x": 0.0, "y": 0.0, "z": 0.0},
"scale": {"x": 2.0, "y": 2.0, "z": 2.0},
"interactionVolume": {
"eventName": "zone_used",
"action": "interact"
}
}

Properties: eventName, action (required)

Volume Size​

All volumes use the scale property to define their size:

  • Scale X: Width
  • Scale Y: Height
  • Scale Z: Depth

The volume is a box centered at the position, with dimensions determined by scale.

Trigger Events​

When a volume fires, the runtime calls Gamemode.triggerEvent / listeners registered with Gamemode.onEvent using the author-supplied eventName (or toggle activate/deactivate names, or sequence completion eventName). Payload typically includes instanceId and a reason string (for example enter, exit, stay, interaction).

There is no automatic volume_<type>_<instanceId> event name pattern.

Handle events in gamemode scripts:

Gamemode.onEvent("zone_used", function(data)
print("Used zone", data.instanceId)
end)

Best Practices​

  • Size volumes appropriately: Not too small (hard to trigger) or too large (triggers unexpectedly)
  • Use appropriate trigger types: Choose the right volume type for your use case
  • Test trigger behavior: Verify volumes work as expected
  • Combine with scripts: Use volumes to trigger script behaviors
  • Organize with worlds: Use world IDs to group related scenes

Next Steps​