@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: apply any queued scene swap → step physics → run systems → run scripts (which see updated transforms + real contacts) → sync script/animation-moved colliders back into physics.
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: Awaken3D 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 a clip on Play start. |
See Audio.
In-game UI
| Symbol | Signature | Description |
|---|---|---|
UINode | ComponentType + interface | A screen-space UI element (kind: text | panel | button | image, anchor, x/y/w/h, text, fontSize, color, bg, opacity, visible, and - for image and animation - image/fit/rotation/scaleX/scaleY/font). Rendered as a DOM overlay in both the editor Game view and the shipped player. |
uiNodeStyle | (n: UINode, scale?) => CSSProps | Pure UINode → CSS map (one source of truth, unit-testable). scale multiplies every pixel dimension (the overlay passes viewportHeight / uiReferenceHeight). |
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 | Button-click handler registry (api.ui.onClick), fired by the overlay. |
UIOverlay | class | The DOM overlay that renders UINodes and dispatches clicks to UIState. |
Scripts drive UI through api.ui: setText / setImage / show / hide / onClick, plus play(name, clip) / stop(name) for UI animation. See UI and Scripting UI.
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), the play window (clipStart/clipEnd), and root-motion 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. |
The pure skinning + blend + root-motion + graph + foot-IK 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 core's simulateEmitter and uploading through deps. burst(effectId, opts?) spawns a transient one-shot that retires itself once exhausted. clear() drops everything. |
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.