@awaken/runtime
Everything that runs while your game plays: the physics engine (Rapier), the script host, the play/scene lifecycle, input, audio, in-game UI, and skeletal-animation playback. It depends on @awaken/core and Rapier (WASM) - not on @awaken/render, so the whole runtime is headless-testable.
ts
import { Runtime, PlayController, ScriptHost, RigidBody, Collider, RapierBackend } from "@awaken/runtime";The Scripting API is the user-facing surface over this package; the Physics section covers the physics feature set. This page is the underlying types.
runtime (systems)
| Symbol | Signature | Description |
|---|---|---|
System | (world, dt) => void | A per-frame simulation function. |
Runtime | class | Holds an array of Systems; tick(world, dt) runs them in order. |
DEFAULT_SYSTEMS | System[] ([]) | Empty by default - gameplay lives in scripts (the ScriptHost) and physics is driven by the PhysicsRunner, not built-in tick systems. |
The play lifecycle
PlayController owns Edit → Play → Stop. On Play it snapshots the scene, builds the physics sim + script host, and starts play-on-start audio; on Stop it tears them down and restores the snapshot (optionally keeping your authored edits). See Preview & Play.
| Symbol | Signature | Description |
|---|---|---|
PlayState | "edit" | "play" | "paused" | The controller's state. |
PlayController | class | play(), pause(), resume(), stop(keepEdits?), update(dt), state, markEdited()/hasEdits(), requestRestart(). Constructed with the world, Runtime, component types, and optional services (registry, audio, hud, physics, ui, scenes, game, prefab source, log sink). |
update(dt) runs the frame in order: scale dt by api.timeScale → apply any queued scene swap → run the fixed steps the frame owes (each: every script's onFixedUpdate, then a physics step when physics is on the fixed clock) → step physics once on the frame's dt when it is not → run systems → run scripts' onUpdate (which see updated transforms + real contacts) → sync script/animation-moved colliders back into physics. See Time & the Fixed Clock.
Fixed clock
| Symbol | Signature | Description |
|---|---|---|
TimeSettings | interface | Project Settings → Time: fixedHz, maxStepsPerFrame, physicsOnFixed, probeDelay, physics2D. |
DEFAULT_TIME_SETTINGS | TimeSettings | { fixedHz: 60, maxStepsPerFrame: 5, physicsOnFixed: false, probeDelay: 2, physics2D: false }. |
FIXED_HZ_RANGE / MAX_STEPS_RANGE / PROBE_DELAY_RANGE | [min, max] | [1, 240], [1, 20], [0, 16]. |
normalizeTimeSettings | (raw: unknown) => TimeSettings | Clamp and default a saved value. |
FixedClock | class | advance(frameDt, catchUp?) returns the steps a frame owes (capped at maxStepsPerFrame, the excess dropped so the game slows instead of skipping steps); refund(steps) gives back steps a host could not run yet; dt, lastSteps, droppedSeconds, reset(). PlayController.clock is the one Play runs on. |
Scripting
The script host runs each entity's Script behaviours and exposes the ScriptApi to them. Script parameters are discovered from the compiled class so the Inspector can edit them.
| Symbol | Signature / Shape | Description |
|---|---|---|
ScriptApi | interface | The full script-facing surface: transform helpers, find/findByTag, spawn, events (emit/on), timers (after/every), loadScene/reloadScene, save/load/state, moveAndSlide, raycast, playSound, setHud, ui, log. Documented field-by-field in Scripting API. |
ScriptBehavior | interface | The lifecycle hooks a script implements: onStart, onUpdate(api, dt), onDestroy, onCollision/onCollisionEnter/onCollisionExit. |
Script | ComponentType + interface | { scripts: ScriptEntry[] } - an entity can carry several behaviours. |
ScriptEntry | { name; params?; refs?; enabled? } | One attached behaviour: its script id, per-instance parameter overrides, entity-reference fields, and enable flag. |
Tag | ComponentType + interface | { value: string } - a string tag for findByTag lookup (find by tag, not by name). |
ScriptParam | { name; type; default; component?; options?; description?; min?; max? } | A discovered public field (drives the Inspector control). |
discoverScriptParams | (factory, source?) => ScriptParam[] | Discover a script's editable public fields by combining instance introspection + source parsing (TS types are erased at runtime). |
ScriptRegistry | class | register(name, factory), get, unregister, names() - the compiled-script lookup. |
ScriptHost | class | Runs the behaviours: update(world, dt, contacts?) and destroyAll(world). Owns per-slot instances, timers, event handlers, and collision-edge tracking; hot-restarts a slot when its params change during Play. |
SpawnFn | (prefab, opts?) => Entity | null | Prefab instantiation hook the app supplies. |
See Script Parameters, Events & Timers, and Input.
Physics
Physics is engine-agnostic: Awaken talks only to PhysicsBackend/PhysicsWorld, implemented today by Rapier (WASM). The ECS components author the bodies; the PhysicsRunner builds and steps the sim during Play.
Components
| Symbol | Signature / Shape | Description |
|---|---|---|
RigidBody | ComponentType + interface | { velocity, useGravity, restitution } (defaults: 0 velocity, gravity on, restitution 0.3). |
Collider | ComponentType + interface | { shapes: ColliderShape[] } - one body, many shapes. |
ColliderShape | { kind, center, size, radius, height, mesh, convex, trigger, enabled, pinned? } | One shape: a primitive or a collision mesh, each independently a trigger/solid/toggleable. |
ColliderKind | "box" | "sphere" | "capsule" | "cylinder" | "mesh" | Shape kind. |
newColliderShape | (kind?) => ColliderShape | Construct a default shape. |
primaryShape | (c) => ColliderShape | The first enabled shape (drives the AABB fallback + character capsule). |
GRAVITY | -9.81 | Default gravity constant. |
detectCollisions | (world) => [Entity, Entity][] | Pure AABB overlap test - the fallback when no physics engine is running. |
physicsSystem | System | The legacy hand-rolled semi-implicit-Euler integrator (superseded by the Rapier runner in Play). |
See Rigidbody, Colliders, and the Character Controller.
Backend & runner
| Symbol | Signature | Description |
|---|---|---|
PhysicsBackend | interface | { name, supportsSoftBody, init(), createWorld(gravity) } - a physics engine implementation. |
PhysicsWorld | interface | A live sim: step, bodies, colliders, joints, raycast, drainContactEvents, moveCharacter, and debug line geometry. |
RapierBackend | class implements PhysicsBackend | The Rapier (WASM) implementation. |
PhysicsRunner | class | Bridges the ECS to a PhysicsWorld: prewarm, start(world), addEntity, step(world, dt), syncColliderPoses, contacts(world), moveCharacter, raycastForScript, plus debugLines/debugTriggers for the physics debug view. |
createMeshShapeFor | (…) => ShapeDesc | null | Build a convex-hull/trimesh collision shape from a mesh. |
BodyType, ShapeDesc, BodyDesc, ColliderDesc, JointDesc, JointMotor, JointSpring, RaycastHit, ContactEvent, CharConfig | types | The backend's descriptor + event vocabulary (see Joints, Queries & Events). |
Scenes & state
| Symbol | Signature | Description |
|---|---|---|
SceneDirector | class | The runtime scene library + deferred transition queue. register/registerAll, load(name), reload(), take() (drained each frame by the PlayController). Loads apply at the top of the next frame, never mid-script. |
GameState | class | state (in-memory session bag surviving scene loads) + save(key, value)/load(key) (namespaced localStorage) + resetSession(). |
KVStore | interface | { getItem, setItem, removeItem } - the storage abstraction, so save/load is testable without a browser. |
See Scenes & Persistence.
Input
| Symbol | Signature | Description |
|---|---|---|
Input | class | Per-frame input: isDown(key), mouseDX/mouseDY (per-frame delta for mouse-look), pointerLocked, plus the setters the game loop drives (setKey, addMouse, clearMouse, setPointerLocked, clear). |
Scripts read this via api.input. See Input.
Audio
| Symbol | Signature | Description |
|---|---|---|
AudioEngine | interface | { play(clip, opts?) } - pluggable so the runtime stays headless-testable. |
NoopAudioEngine | class | The headless no-op backend. |
PlayOpts | { volume?, loop? } | Playback options. |
AudioSource | ComponentType + interface | { clip, volume, loop, playOnStart } - an entity that plays an imported sound on Play start. |
BrowserAudioEngine | class | The Web Audio backend, shared by the editor and the exported player: addSound (Opus, Ogg, MP3, WAV, M4A/AAC and MIDI bytes), play (returns a handle), stop, stopAll, setMasterVolume, setVolume, setTempo. |
See Audio.
Jobs and GPU compute
| Symbol | Signature | Description |
|---|---|---|
JobPool | class | The worker pool that runs compiled WebAssembly module entries off the main thread (api.jobs). |
Job | { result: Promise<Uint8Array>, cancel() } | One queued run. |
JobCancelled | class extends Error | What a cancelled job's result rejects with. |
JobsApi | interface | run(entry, input) / run(moduleId, entry, input). |
GpuApi / GpuHost | interface | api.gpu, and what a host supplies for it (@awaken/render's GpuCompute fits it). See GPU compute. |
SimulationHandle / ProbeHandle | interface | api.simulation(entity) and api.probe(entity). |
The app: channel, haptics, safe area and events
| Symbol | Signature | Description |
|---|---|---|
APP_CHANNELS / DEFAULT_APP_CHANNEL | ["release", "beta", "dev"] / "dev" | The release channel a host reports (api.app.channel); dev in the editor and headless runs. |
HAPTIC_KINDS | ["select", "tap", "heavy", "success", "warning", "error"] | Every haptic kind, weakest first. |
HapticGate | class | Rate limit shared by every script of a host: one haptic per gap, except a stronger kind than the last passes anyway. |
SafeArea / NO_SAFE_AREA | { top, right, bottom, left } | Insets in CSS px that UI keeps clear of (api.input.safeArea); all zero on desktop. |
LAUNCH_EVENT, BACKGROUND_EVENT, FOREGROUND_EVENT, CLOSE_EVENT, MEMORY_WARNING_EVENT, ERROR_EVENT | string | The engine's own events on the script event bus: awaken:launch, awaken:background, awaken:foreground, awaken:close, awaken:memory-warning, awaken:error. |
EngineError / ENGINE_ERROR_KINDS | interface / list | awaken:error's payload { kind, message, stack }; kinds script, gpu, save, save-read, store, outbox, page. Each error is raised once, then at most every 10 seconds with the same text. |
StoreApi | interface | api.store, in-app purchases. See In-app purchases. |
OutboxApi / OutboxSink | interface | api.outbox, and what the app does with it (the mobile shell's queue, the player page's own, or the editor's Console). See Outbox & App Events. |
In-game UI
| Symbol | Signature | Description |
|---|---|---|
UINode | ComponentType + interface | A screen-space UI element (kind: text | panel | button | image). Rendered as a DOM overlay in both the editor Game view and the shipped player. Fields: Component Reference. |
UIScreen | ComponentType + interface | Marks an object as a screen: one page of UI shown and hidden as one, with layout-variant limits, edge insets, a design size and followParent. Fields: Component Reference. |
screenOf / screenShowing / screensByName | helpers | A node's screen (nearest UIScreen ancestor, itself included); whether a screen shows at a view size (its flags, the layout variant chosen, and for a nested screen the screen above it); every screen with a name. |
chooseVariant / variantChosen | helpers | The layout-variant rule: which of a screen's same-named variants a view size gets. See In-Game UI. |
screenScale / gameAreaInsets | helpers | A screen's UI scale at a view size (its design size, else the fallback); the edges a Fit-contain camera keeps clear of (safe area plus the showing screens' insets). |
uiNodeStyle | (n, scale?, cw?, ch?, moving?, safe?, layout?) => CSSProps | Pure UINode → CSS map (one source of truth, unit-testable). scale multiplies every pixel dimension; layout.nested lays it out in its parent element's box. |
UIAnimator | ComponentType + interface | { clip, playing, speed, time } - plays a UIClip on a widget's UINodes (tracks address them by name). |
driveUIAnimators | (world, dt, clipById) => void | Advance every UIAnimator and write its clip's sampled values onto the widget's UINodes. Call once per frame before the overlay syncs. |
applyUIClip | (clip, time, resolve) => void | Sample every track of a UIClip at time and write the values onto the resolved target nodes (loops if the clip loops). |
sampleTrack / clipDuration | helpers | Linear-interpolate a keyframe list at t; the latest keyframe time across a clip's tracks. |
UIClip / UITrack / UIKey / UIProp | types (re-exported from @awaken/core) | The renderer-agnostic keyframe model a UIAnimator plays (imported from Unity .anim). |
HudSink / NoopHudSink | interface / class | The HUD-text sink (api.setHud). |
UIState | class | The handler registry for api.ui.onClick / onPress / onPointer, fired by the overlay, plus the host's hitTester and rectFinder behind api.ui.hitTest and api.ui.rect. |
UIOverlay | class | The DOM overlay that renders UINodes and dispatches presses to UIState; hitTest(x, y) and rectOf(world, name) answer from the laid-out page. |
Scripts drive UI through api.ui by element and screen name. See UI and Scripting UI for the full list.
Animator & animation system
| Symbol | Signature | Description |
|---|---|---|
Animator | ComponentType + interface | Per-entity playback state: clip/skeleton/time/speed/loop/playing, the crossfade fields (fadeClip/fadeTime/fadeDuration/fadeElapsed/fadeLoop/fadeSpeed), the play window (clipStart/clipEnd), and root motion (applyRootMotion, output rootDx/rootDz, flatRootY). |
playClip | (anim, clip, opts?) => void | Start playback, crossfading over opts.fade (blends the two clips via core blendPoses); idempotent. |
advanceTime / advanceTimeRange | clock helpers | Advance a playback clock (loops both directions; non-looping clamps); the Range form trims to a [start,end] window. |
animationSystem | (world, dt, deps: AnimationDeps) => void | Advance every Animator, crossfade-blend, extract root motion, apply foot IK, CPU-skin its mesh, and upload the deformed positions; also writes bone-anchor poses and builds/drives a ragdoll when one is flagged. |
AnimationDeps | interface | { clip, skeletonFor, skinFor, upload, footIK? } - outside-the-ECS dependencies, injected for testability (footIK supplies the ground source + config). |
AnimStateMachine / animGraphSystem | ComponentType / (world, deps) => void | Param-driven state graph that chooses the Animator's clip; LOCOMOTION_GRAPH is a built-in. Drives via api.setAnimParam/setAnimTrigger. |
BoneAnchor | ComponentType | Mirrors one named skeleton bone onto an entity so attachments ride the animation. |
Ragdoll / buildRagdollBodies / ragdollBoneMatrices | component + helpers | Hands a skinned character to physics (joint-linked bodies) and drives the skeleton from the sim. Experimental. |
| applyFootIK / FootIKConfig | (skel, pose, cfg, groundY, weight?) => Mat4[] | Two-bone leg IK, pelvis adjust and plant weight, grounding a planted foot. A per-frame solver, so it sits here rather than in core; only quatFromTo (shared with the ragdoll) is core math. |
The pure skinning, blend, root-motion and graph math lives in @awaken/core. See Animation.
Particle engine
| Symbol | Signature | Description |
|---|---|---|
ParticleEngine | class | Owns every live emitter pool and every in-flight one-shot burst. tick(world, dt) advances each active ParticleEmitter (and each burst), simulating via simulateEmitter and uploading through deps. burst(effectId, opts?) spawns a transient one-shot that retires itself once exhausted. clear() drops everything. |
EmitterState | SoA pool | One emitter's live particles (position, velocity, life, rotation, size arrays) plus a seeded PRNG. Side-state, never serialized. |
makeEmitterState | (cfg, seed?) => EmitterState | Allocate a pool sized to cfg.maxParticles with a deterministic PRNG, never Math.random. |
simulateEmitter | (state, cfg, dt, worldMatrix, out) => count | Advance by dt and write count x PARTICLE_FLOATS floats into out; returns the live count. out must hold state.capacity x PARTICLE_FLOATS. |
ParticleDeps | interface | { effect(id), upload(handle, data, count, opts), remove(handle) } - the seam that keeps @awaken/runtime free of any renderer or asset-store import. effect resolves an id against the project; upload/remove are backed by the renderer's particle buffers. |
Pools are side-state keyed by entity: they are never serialized, and they bypass the ECS dirty-set entirely - the same rule CPU skinning follows, so a per-frame particle update never triggers a draw-cache rebuild. A burst is deliberately not tied to the script that fired it, so destroying an object mid-frame doesn't cut off its own explosion. See Particles.
See also
- Scripting API - the user-facing surface over
ScriptApi/ScriptBehavior. - Physics Overview - the physics feature set backed by
PhysicsRunner/Rapier. - Component Reference -
RigidBody,Collider,Script,Tag,AudioSource,Animator,UINode,UIAnimatorfields. - Preview & Play - the play lifecycle
PlayControllerdrives. - @awaken/core - the
World,Entity, and animation math the runtime builds on.