Skip to content

Scripting Overview

Scripting is how you add behaviour to a Awaken game - you write a small TypeScript class, attach it to an object, and it runs every frame during Play.

Awaken scripting is deliberately close to Unity's MonoBehaviour model, but with a browser-native twist: there is no compiler on disk and no build step. You author TypeScript in the Code panel, press Compile, and the class is registered into a live registry that both editor Play and every shipped export run. The game you play in the editor runs the same runtime, from the same script source, as the game you ship - only the module wrapper differs (ESM in the editor, a self-contained IIFE in an export; see Writing & Compiling).

The model

A script asset is one authored class. It is stored as { name, source, kind? } under a stable id (a UUID), never under its display name. That indirection is the whole trick behind rename-safe scripts: components reference the id, so renaming Rotator to Spinner in the Code panel never detaches it from the objects running it.

ts
interface ScriptAsset {
  name: string;                     // display name (renamable, cosmetic)
  source: string;                   // the TypeScript you wrote
  kind?: "runtime" | "editor" | "plugin"; // absent = "runtime". "editor" = a one-off tool; "plugin" = a Plugin
}

You attach behaviour through the Script component, which holds a list of entries - an object can run several behaviours at once:

ts
interface Script { scripts: ScriptEntry[] }

interface ScriptEntry {
  name: string;                     // the script asset's stable id (NOT its display name)
  params?: Record<string, ...>;     // per-instance overrides for public fields
  refs?: Record<string, Entity>;    // per-instance entity references
  enabled?: boolean;                // toggle this one behaviour
}

So a crate can carry a Floater, a Spinner, and a Pickup all at once, each with its own knobs. Every entry is an independent slot - keyed `${entity}:${index}` at runtime - with its own instance, its own timers, and its own event handlers, so disabling or restarting one never disturbs its siblings.

Runtime vs editor scripts

The kind field splits scripts into two worlds:

kindRuns duringShipped in export?Purpose
"runtime" (default)Play, Preview, and the exported gameYesGameplay behaviour
"editor"Only inside the editor as a toolNever - stripped from every exportScene automation (bulk rename, combine, cleanup)

Editor-only scripts are the subject of Editor-Only Scripts; the rest of this section is about runtime gameplay scripts.

The behaviour class and its lifecycle

A runtime script is a class that export defaults and implements some of the ScriptBehavior hooks. Every hook receives the same api object - your entire gameplay surface (see the ScriptApi Reference).

ts
// Component behavior - runs during Play. Type 'api.' for autocomplete.
export default class implements ScriptBehavior {
  onUpdate(api: ScriptApi, dt: number) {
    api.rotate(0, 1, 0, dt); // spin about world-Y, 1 rad/s
  }
}

The hooks fire on a strict, per-slot lifecycle:

HookSignatureWhen
onStart(api)Once, the first frame the slot becomes live
onUpdate(api, dt)Every frame, dt = seconds since last frame
onCollisionEnter(api, other)First frame this collider begins overlapping other
onCollision(api, other)Every frame the overlap persists
onCollisionExit(api, other)First frame the overlap ends
onDestroy(api)The slot tears down - entity destroyed, disabled, or Stop

All hooks are optional; implement only what you need. Collision hooks fire for both solid hits and trigger (sensor) overlaps - the collider's trigger flag decides physical blocking, not which hook runs. See Events & Timers and Physics: Queries & Events.

Where scripts run

Scripts execute inside the PlayController, which is the same object in the editor, in Preview, and in a shipped game.html. Each frame it runs a fixed order:

Because scripts run after physics and animation each frame, they see up-to-date transforms and real engine contacts. When physics is not running (a scene with no colliders), collision hooks fall back to an AABB overlap test so triggers still fire.

One editor nicety: while Play is running you can tweak a script's params live in the Inspector. Awaken treats a param edit as a restart - it resets that slot to its play-start pose and re-runs onStart - so the preview always reflects "the game from the beginning", not a half-finished run.

From public field to Inspector control

You almost never hardcode tuning values. Declare a public class field and Awaken turns it into an editable control on the Script component, per instance:

ts
export default class implements ScriptBehavior {
  speed = 2;                  // number box
  turnRate = 90;              // spin degrees 0..360   → slider (min..max in the comment)
  clockwise = true;           // checkbox
  mode: "loop" | "ping";      // dropdown (string-literal union)
  target: Entity;             // object picker
  camera: Camera;             // object picker filtered to cameras

  onUpdate(api: ScriptApi, dt: number) {
    api.rotate(0, this.clockwise ? 1 : -1, 0, this.speed * dt);
  }
}

The exact rules - sliders, enums, vec3s, entity references, tooltips, defaults - are covered in Inspector Parameters.

Author → compile → register → Play

Editing the source auto-saves into the project on every keystroke, but only Compile rebuilds the runnable class and refreshes its Inspector params. See Writing & Compiling.

In this section

  • Writing & Compiling - the Code panel, the template, Monaco IntelliSense, and how Compile registers a class.
  • ScriptApi Reference - every api.* method and every ScriptBehavior hook, with examples. The core reference.
  • Inspector Parameters - how public fields become sliders, enums, vec3s, and object pickers.
  • Events & Timers - emit/on messaging and after/every scheduling.
  • Input - keyboard, mouse-look deltas, and pointer lock.
  • UI from Scripts - driving UINode elements and the HUD during Play.
  • Scenes & Persistence - level transitions, saved progress, and the session state bag.
  • Editor-Only Scripts - the kind:"editor" tool scripts that never ship.
  • Plugins - the kind:"plugin" scripts that add editor panels, tools, and commands.
  • Examples - complete, runnable behaviours you can paste in.

See also

Awaken — browser-native WebGPU game engine.