gameable
Classes
BodyIndex
Maps host body ids back onto entities and ingests frame-input.bodies.
The incoming list is a Float32Array in both modes — jco lifts WIT list<f32> into one, and direct mode hands the guest the host's own buffer (see packages/sdk/src/wit/generated/interfaces/gameable-engine-game.d.ts). It is read purely by index and never copied.
Constructors
Constructor
ts
new BodyIndex(maxBodies): BodyIndex;Parameters
| Parameter | Type | Description |
|---|---|---|
maxBodies | number | Body-id ceiling. |
Returns
Properties
bodyToEntity
ts
readonly bodyToEntity: Uint32Array;Body id to entity id. Index 0 is unused: body 0 means "no body".
lastRowCount
ts
lastRowCount: number = 0;Number of rows seen in the most recent ingest.
Methods
bind()
ts
bind(body, entity): void;Associate a body with the entity it drives.
Parameters
| Parameter | Type | Description |
|---|---|---|
body | number | Body id. |
entity | number | Entity id. |
Returns
void
Nothing.
bodyOf()
ts
bodyOf(entity): number;The body id driving an entity, or 0.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity id. |
Returns
number
The body id, or 0 when the entity has none.
clear()
ts
clear(): void;Forget every body.
Returns
void
Nothing.
ingest()
ts
ingest(bodies): void;Copy post-step body transforms into Transform and Velocity.
This does not mark the entity moved, and that is the point. The host owns a body-driven entity's transform: the physics module hands the same rows straight to the adapter after it steps, so the object in the scene is already where the body is. Marking here would send all twelve floats back across the boundary in frame-output.transforms for the host to write a second time — the same numbers, one step later.
What the guest gets is still the truth for gameplay: read Transform.x[e] to aim at something, Velocity to decide whether it is running. A system that overwrites those lanes is authoring a move rather than observing one, and has to say so with markMoved — see markMoved.
Parameters
| Parameter | Type | Description |
|---|---|---|
bodies | ArrayLike<number> | The packed rows, stride 15. |
Returns
void
Nothing.
unbind()
ts
unbind(body): void;Forget a body.
Parameters
| Parameter | Type | Description |
|---|---|---|
body | number | Body id. |
Returns
void
Nothing.
CommandBuffer
Builds and owns one frame's commands list.
Constructors
Constructor
ts
new CommandBuffer(): CommandBuffer;Returns
Properties
list
ts
readonly list: Command[] = [];The list handed to the host. Reused every frame; never retain it.
Methods
addBody()
ts
addBody(
body,
entity,
kind,
shape,
hx,
hy,
hz,
px,
py,
pz,
mass,
layer,
mask,
flags
): AddBodyCmd;Create a rigid body or character controller.
Parameters
| Parameter | Type | Description |
|---|---|---|
body | number | Guest-minted body id. |
entity | number | Entity the body drives. |
kind | BodyKind | Body class. |
shape | ShapeKind | Collision shape family. |
hx | number | Half-extent / radius lane 0. |
hy | number | Half-extent / half-height lane 1. |
hz | number | Half-extent lane 2. |
px | number | Position x. |
py | number | Position y. |
pz | number | Position z. |
mass | number | Kilograms; ignored for fixed and kinematic bodies. |
layer | CollisionLayers | What this body is. |
mask | CollisionLayers | What this body collides with. |
flags | BodyFlags | Per-body switches. |
Returns
The payload, so the caller can tune friction and damping. Round anything written onto it with Math.fround.
applyImpulse()
ts
applyImpulse(
body,
x,
y,
z,
atX?,
atY?,
atZ?
): void;Apply a one-shot impulse.
Parameters
| Parameter | Type | Description |
|---|---|---|
body | number | Body id. |
x | number | Impulse x. |
y | number | Impulse y. |
z | number | Impulse z. |
atX? | number | World-space application point x. Omit for the centre of mass. |
atY? | number | Application point y. |
atZ? | number | Application point z. |
Returns
void
Nothing.
conversation()
ts
conversation(
entity,
action,
character?,
text?
): void;Control an optional host conversation module using pooled commands.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
entity | number | undefined | Interviewed entity. |
action | | "start" | "end" | "ask" | "interrupt" | "microphone-on" | "microphone-off" | undefined | Lifecycle or text command. |
character | string | '' | Configured character id, never a URL. |
text | string | '' | Typed/transcribed question; empty for lifecycle commands. |
Returns
void
despawn()
ts
despawn(entity): void;Destroy an entity in the host scene.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity id. |
Returns
void
Nothing.
loadAsset()
ts
loadAsset(asset, priority): void;Ask the host to start loading an asset.
Parameters
| Parameter | Type | Description |
|---|---|---|
asset | number | Asset handle. |
priority | number | Higher runs first. |
Returns
void
Nothing.
lookAt()
ts
lookAt(
entity,
target,
weight
): void;Aim a character's head and eyes.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity id. |
target | Vec3 | null | World-space point, or null to release the look-at. |
weight | number | Blend weight in 0..1. |
Returns
void
Nothing.
moveCharacter()
ts
moveCharacter(
body,
vx,
vy,
vz,
jump,
crouch,
maxSlopeDeg
): void;Drive a character body for one step.
Parameters
| Parameter | Type | Description |
|---|---|---|
body | number | Body id of a character body. |
vx | number | Desired velocity x. |
vy | number | Desired velocity y. |
vz | number | Desired velocity z. |
jump | boolean | Request a jump this step. |
crouch | boolean | Request a crouch this step. |
maxSlopeDeg | number | Maximum walkable slope in degrees. |
Returns
void
Nothing.
playSound()
ts
playSound(
sound,
asset,
entity,
volume,
pitch,
looping,
bus
): void;Start a sound.
Parameters
| Parameter | Type | Description |
|---|---|---|
sound | number | Guest-minted sound handle. |
asset | number | Audio asset handle. |
entity | number | undefined | Entity to follow, or undefined for a non-positional sound. |
volume | number | Linear gain in 0..1. |
pitch | number | Playback-rate multiplier. |
looping | boolean | Loop the sound. |
bus | AudioBus | Mixer bus. |
Returns
void
Nothing.
removeBody()
ts
removeBody(body): void;Destroy a body.
Parameters
| Parameter | Type | Description |
|---|---|---|
body | number | Body id. |
Returns
void
Nothing.
reset()
ts
reset(): void;Rewind for a new frame. Keeps every pooled object alive.
Returns
void
Nothing.
say()
ts
say(
entity,
text,
audio?,
visemes?
): void;Speak a line.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity id. |
text | string | Subtitle text. |
audio? | number | Voice line asset handle. |
visemes? | string | Viseme track as JSON. |
Returns
void
Nothing.
setAnim()
ts
setAnim(
entity,
clip,
looping,
speed,
fadeMs,
weight
): void;Play or cross-fade a clip.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity id. |
clip | string | Clip name inside the entity's asset. |
looping | boolean | Loop the clip. |
speed | number | Playback rate multiplier. |
fadeMs | number | Cross-fade duration in milliseconds. |
weight | number | Target layer weight in 0..1. |
Returns
void
Nothing.
setAsset()
ts
setAsset(entity, asset): void;Attach or detach a renderable.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity id. |
asset | number | undefined | Asset handle, or undefined to detach. |
Returns
void
Nothing.
setBodyEnabled()
ts
setBodyEnabled(body, enabled): void;Enable or disable a body in the broad phase.
Parameters
| Parameter | Type | Description |
|---|---|---|
body | number | Body id. |
enabled | boolean | Whether the body participates. |
Returns
void
Nothing.
setBodyTransform()
ts
setBodyTransform(
body,
px,
py,
pz,
qx,
qy,
qz,
qw,
teleport
): void;Move a body directly.
Parameters
| Parameter | Type | Description |
|---|---|---|
body | number | Body id. |
px | number | Position x. |
py | number | Position y. |
pz | number | Position z. |
qx | number | Rotation x. |
qy | number | Rotation y. |
qz | number | Rotation z. |
qw | number | Rotation w. |
teleport | boolean | Clear velocities and skip interpolation. |
Returns
void
Nothing.
setBodyVelocity()
ts
setBodyVelocity(
body,
x,
y,
z,
ax?,
ay?,
az?
): void;Overwrite a body's velocity.
Parameters
| Parameter | Type | Description |
|---|---|---|
body | number | Body id. |
x | number | Linear x. |
y | number | Linear y. |
z | number | Linear z. |
ax? | number | Angular x, radians per second. Omit to leave spin alone. |
ay? | number | Angular y. |
az? | number | Angular z. |
Returns
void
Nothing.
setCharacterState()
ts
setCharacterState(
entity,
state,
vx,
vy,
vz,
grounded
): void;Drive a character's locomotion state machine.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity id. |
state | string | State name, for example `'idle' |
vx | number | Velocity x. |
vy | number | Velocity y. |
vz | number | Velocity z. |
grounded | boolean | Whether the character is on the ground. |
Returns
void
Nothing.
setClipWeights()
ts
setClipWeights(
entity,
clips,
weights,
timeScale
): void;Set explicit per-clip weights.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity id. |
clips | readonly string[] | Clip names. |
weights | readonly number[] | Float32Array<ArrayBufferLike> | Positional weights; must match clips in length. Copied into pooled storage, so the caller may reuse its own array freely. |
timeScale | number | Playback rate for the whole layer. |
Returns
void
Nothing.
setExpression()
ts
setExpression(
entity,
space,
weights
): void;Set facial expression coefficients.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity id. |
space | ExpressionSpace | Coordinate space the weights are in. |
weights | readonly number[] | Float32Array<ArrayBufferLike> | Coefficients; length must match the space. Copied into pooled storage, so the caller may reuse its own array freely. |
Returns
void
Nothing.
setListener()
ts
setListener(
px,
py,
pz,
qx,
qy,
qz,
qw
): void;Place the audio listener.
Parameters
| Parameter | Type | Description |
|---|---|---|
px | number | Position x. |
py | number | Position y. |
pz | number | Position z. |
qx | number | Rotation x. |
qy | number | Rotation y. |
qz | number | Rotation z. |
qw | number | Rotation w. |
Returns
void
Nothing.
setMaterialParam()
ts
setMaterialParam(
entity,
name,
value
): void;Set one material uniform.
The value is copied into a pooled holder, rounded to f32: the host never sees the guest's own object, and a colour built inline costs nothing after the first frame. Only a slot that changes variant allocates.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity id. |
name | string | Uniform name. |
value | MaterialValue | The value variant. |
Returns
void
Nothing.
setParent()
ts
setParent(
entity,
parent,
keepWorldTransform
): void;Reparent an entity.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity id. |
parent | number | undefined | New parent, or undefined for the scene root. |
keepWorldTransform | boolean | Preserve the world transform across the move. |
Returns
void
Nothing.
setPointerLock()
ts
setPointerLock(locked): void;Request or release pointer lock.
Parameters
| Parameter | Type | Description |
|---|---|---|
locked | boolean | Whether the canvas should hold pointer lock. |
Returns
void
Nothing.
setTimeScale()
ts
setTimeScale(scale): void;Scale simulated time.
Parameters
| Parameter | Type | Description |
|---|---|---|
scale | number | Multiplier; 1 is real time. |
Returns
void
Nothing.
spawn()
ts
spawn(
entity,
asset,
px,
py,
pz,
qx,
qy,
qz,
qw,
sx,
sy,
sz,
name?
): void;Create an entity in the host scene.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Guest-minted entity id. |
asset | number | undefined | Renderable asset handle, or undefined for a bare node. |
px | number | Position x. |
py | number | Position y. |
pz | number | Position z. |
qx | number | Rotation x. |
qy | number | Rotation y. |
qz | number | Rotation z. |
qw | number | Rotation w. |
sx | number | Scale x. |
sy | number | Scale y. |
sz | number | Scale z. |
name? | string | Debug label. |
Returns
void
Nothing.
spawnCharacter()
ts
spawnCharacter(
entity,
bundle,
px,
py,
pz,
qx,
qy,
qz,
qw
): void;Instantiate a splat character bundle.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Guest-minted entity id. |
bundle | number | Character bundle asset handle. |
px | number | Position x. |
py | number | Position y. |
pz | number | Position z. |
qx | number | Rotation x. |
qy | number | Rotation y. |
qz | number | Rotation z. |
qw | number | Rotation w. |
Returns
void
Nothing.
stopSound()
ts
stopSound(sound, fadeMs): void;Stop a playing sound.
Parameters
| Parameter | Type | Description |
|---|---|---|
sound | number | Sound handle. |
fadeMs | number | Fade-out in milliseconds. |
Returns
void
Nothing.
take()
ts
protected take<T>(tag, make): T;Take the next pooled slot for a tag, appending it to the frame list.
Type Parameters
| Type Parameter |
|---|
T |
Parameters
| Parameter | Type | Description |
|---|---|---|
tag | | "spawn" | "send" | "set-player-camera" | "set-player-hud" | "save-player-data" | "save-game-data" | "set-player-entity" | "exchange" | "conversation" | "despawn" | "set-asset" | "set-parent" | "set-anim" | "set-material-param" | "add-body" | "remove-body" | "set-body-transform" | "set-body-velocity" | "apply-impulse" | "set-body-enabled" | "move-character" | "spawn-character" | "set-character-state" | "set-clip-weights" | "set-expression" | "look-at" | "say" | "play-sound" | "stop-sound" | "set-listener" | "load-asset" | "set-pointer-lock" | "set-time-scale" | The command tag. |
make | () => T | Module-const factory for a fully shaped payload, called only when the pool has to grow. |
Returns
T
The payload to mutate. Every field must be written: the slot still holds whatever the last command with this tag left behind.
MessageDef
One game message: what defineMessage returns. Pass it to ctx.net.messages and ctx.net.send.
Example
ts
const Ready = defineMessage('ready', (p): p is true => p === true);
console.log(Ready.name, Ready.maxBytes); // 'ready' 2048Type Parameters
| Type Parameter |
|---|
T |
Constructors
Constructor
ts
new MessageDef<T>(
name,
check,
maxBytes
): MessageDef<T>;Parameters
| Parameter | Type | Description |
|---|---|---|
name | string | The message name on the wire. |
check | MessageCheck<T> | The payload's type guard. |
maxBytes | number | The payload cap in UTF-8 bytes of JSON. |
Returns
MessageDef<T>
Properties
check
ts
readonly check: MessageCheck<T>;The payload's type guard.
maxBytes
ts
readonly maxBytes: number;The payload cap in UTF-8 bytes of JSON.
name
ts
readonly name: string;The message name on the wire.
TransformPacker
Packs entity transforms into frame-output.transforms.
One row is written per entity whose dirty flags are non-zero, in ascending entity order — deterministic, and cheaper than a bitecs query for the densely packed id space the SDK mints.
Constructors
Constructor
ts
new TransformPacker(maxEntities): TransformPacker;Parameters
| Parameter | Type | Description |
|---|---|---|
maxEntities | number | Entity ceiling; the buffer holds this many rows. |
Returns
Properties
dirty
ts
readonly dirty: Uint8Array;Per-entity transform-flags accumulated since the last pack.
highWater
ts
highWater: number = 0;Highest entity id pack scans to.
It rises when a higher id is marked and falls back to the last dirty id every pack, so a level that spawned 4,000 entities and despawned all but ten does not keep scanning 4,000 slots a frame.
Methods
clear()
ts
clear(): void;Forget every pending flag, for example after restore.
Returns
void
Nothing.
mark()
ts
mark(entity, flags): void;Accumulate dirty flags for one entity.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity id. |
flags | number | Any combination of TRANSFORM_FLAGS. |
Returns
void
Nothing.
pack()
ts
pack(): Float32Array;Write every dirty entity into the packed buffer and clear the flags.
Returns
Float32Array
A memoised subarray of the internal buffer. The same row count always returns the same object, which is what makes a steady-state tick allocation-free; never retain it across frames.
Interfaces
AddBodyCmd
Create a rigid body or character controller.
Properties
angularDamping
ts
angularDamping: number;body
ts
body: number;entity
ts
entity: number;flags
ts
flags: BodyFlags;friction
ts
friction: number;kind
ts
kind: BodyKind;layer
ts
layer: CollisionLayers;linearDamping
ts
linearDamping: number;mask
ts
mask: CollisionLayers;mass
ts
mass: number;position
ts
position: Vec3;restitution
ts
restitution: number;rotation
ts
rotation: Quat;shape
ts
shape: Shape;AnimEventData
An animation clip passed a named marker.
Properties
clip
ts
clip: string;entity
ts
entity: number;name
ts
name: string;time
ts
time: number;ApplyImpulseCmd
Apply a one-shot impulse.
Properties
atPoint?
ts
optional atPoint?: Vec3;body
ts
body: number;impulse
ts
impulse: Vec3;AssetDesc
Manifest metadata for one asset handle.
Properties
hasCollider
ts
hasCollider: boolean;id
ts
id: number;kind
ts
kind: AssetKind;name
ts
name: string;ready
ts
ready: boolean;rig?
ts
optional rig?: string;tags
ts
tags: readonly string[];AssetLoadedEvent
An asset finished loading.
Properties
asset
ts
asset: number;name
ts
name: string;Axis2
A two-axis reading. Each facade returns its own object, the same one every call.
Properties
x
ts
x: number;y
ts
y: number;BodyFlags
Per-body behaviour switches. Omitted keys lower as false.
Properties
ccd?
ts
optional ccd?: boolean;debugDraw?
ts
optional debugDraw?: boolean;lockRotation?
ts
optional lockRotation?: boolean;noSleep?
ts
optional noSleep?: boolean;reportContacts?
ts
optional reportContacts?: boolean;sensor?
ts
optional sensor?: boolean;BodySpec
The physics body a prefab carries.
Properties
dims?
ts
optional dims?: readonly number[];Shape dimensions. box: half extents. sphere: [radius]. capsule / cylinder: [radius, halfHeight]. plane: the normal.
flags?
ts
optional flags?: BodyFlags;Per-body switches.
friction?
ts
optional friction?: number;Coulomb friction. Default 0.5.
kind
ts
kind: BodyKind;Body class. 'character' makes a CharacterVirtual controller.
layer?
ts
optional layer?: CollisionLayers;What this body is. Default { defaultLayer: true }.
mask?
ts
optional mask?: CollisionLayers;What this body collides with. Default every layer.
mass?
ts
optional mass?: number;Kilograms; ignored for fixed and kinematic bodies. Default 1.
restitution?
ts
optional restitution?: number;Bounciness in 0..1. Default 0.
shape
ts
shape: ShapeKind;Collision shape family.
CameraState
The camera the host should render from this frame.
Properties
armLength
ts
armLength: number;far
ts
far: number;follow?
ts
optional follow?: number;fovYDeg
ts
fovYDeg: number;mode
ts
mode: CameraMode;near
ts
near: number;offset
ts
offset: Vec3;position
ts
position: Vec3;projection
ts
projection: ProjectionKind;rotation
ts
rotation: Quat;target?
ts
optional target?: Vec3;CharacterReadyEvent
A character bundle finished loading and reported its expression space.
Properties
bundle
ts
bundle: number;entity
ts
entity: number;expressionDim
ts
expressionDim: number;space
ts
space: ExpressionSpace;CharacterStore
Splat character bundle handle.
Properties
bundle
ts
bundle: Uint32Array;Character bundle asset handle.
dirty
ts
dirty: Uint8Array;Non-zero when the host has not yet seen the current value.
CollisionLayers
Broad-phase layer set. Omitted keys lower as false.
Properties
character?
ts
optional character?: boolean;debris?
ts
optional debris?: boolean;defaultLayer?
ts
optional defaultLayer?: boolean;enemy?
ts
optional enemy?: boolean;pickup?
ts
optional pickup?: boolean;player?
ts
optional player?: boolean;projectile?
ts
optional projectile?: boolean;staticGeometry?
ts
optional staticGeometry?: boolean;trigger?
ts
optional trigger?: boolean;user0?
ts
optional user0?: boolean;user1?
ts
optional user1?: boolean;user2?
ts
optional user2?: boolean;user3?
ts
optional user3?: boolean;user4?
ts
optional user4?: boolean;user5?
ts
optional user5?: boolean;water?
ts
optional water?: boolean;Contact
One reported contact between two bodies.
Properties
a
ts
a: number;b
ts
b: number;entityA
ts
entityA: number;entityB
ts
entityB: number;impulse
ts
impulse: number;normal
ts
normal: Vec3;phase
ts
phase: ContactPhase;point
ts
point: Vec3;ConversationCmd
Structural interview controls; service addresses stay in host configuration.
Properties
action
ts
action:
| "start"
| "end"
| "ask"
| "interrupt"
| "microphone-on"
| "microphone-off";character
ts
character: string;entity
ts
entity: number;text
ts
text: string;ConversationEvent
Low-frequency conversation UI/input event. No audio or facial frames cross WIT.
Properties
entity
ts
entity: number;kind
ts
kind: "error" | "status" | "input" | "subtitle" | "story";text
ts
text: string;Story events carry the versioned full snapshot as JSON.
DataFacade
The ctx.data facade, one per guest. Only the authority writes: on a client every call is a no-op (logged once).
Saving is not free: save serialises the document. Call it when the document changed, not every tick; the room writes at most one per player every 6 s anyway, and always when the player leaves or the room closes.
Example
ts
function earn(ctx: GameContext): void {
for (const [id, p] of ctx.players) {
const doc = (p.data ?? { coins: 0 }) as { coins: number };
if (ctx.frame % 600 === 0) ctx.data.save(id, { ...doc, coins: doc.coins + 1 });
}
for (const r of ctx.data.results()) if (!r.ok) ctx.net.send('trade-failed', { id: r.id, why: r.reason });
}Accessors
game
Get Signature
ts
get game(): unknown;Returns
unknown
The game's own saved document, once the room has handed it over; else null.
Methods
exchange()
ts
exchange(
a,
b,
give,
take
): number;Trade between two players, all or nothing: a gives b everything in give and takes from b everything in take (numbers move amounts, lists move items; see applyTransfer). The result arrives as an exchange-result event a tick or more later, in DataFacade.results; on success both players' data already show the trade.
Parameters
| Parameter | Type | Description |
|---|---|---|
a | number | The first player. |
b | number | The second player. |
give | TransferDoc | What a gives b, such as { coins: 5 }. |
take | TransferDoc | What a takes from b, such as { owned: ['gem'] }. |
Returns
number
The exchange's id, or 0 on a client.
results()
ts
results(): readonly Pick<ExchangeResultEvent, "id" | "ok" | "reason">[];Returns
readonly Pick<ExchangeResultEvent, "id" | "ok" | "reason">[]
This tick's exchange results, in arrival order; reused, read during the tick.
save()
ts
save(player, doc): void;Keep a player's document. ctx.players.get(player).data is the new document at once; the room writes it to its store (throttled).
Parameters
| Parameter | Type | Description |
|---|---|---|
player | number | A joined player. |
doc | unknown | Any JSON value; at most 64 KB as JSON. |
Returns
void
saveGame()
ts
saveGame(doc): void;Keep the game's own document (one per game, shared by every room).
Parameters
| Parameter | Type | Description |
|---|---|---|
doc | unknown | Any JSON value; at most 64 KB as JSON. |
Returns
void
ExchangeCmd
Trade between two players' documents; the result is an exchange-result event.
Properties
a
ts
a: number;b
ts
b: number;give
ts
give: string;JSON: what a gives b.
id
ts
id: number;Guest-minted; echoed in the result.
take
ts
take: string;JSON: what a takes from b.
ExchangeResultEvent
The outcome of an exchange command, matched by its id.
Properties
aData
ts
aData: string;Player a's document after the trade, as the store wrote it (JSON); empty unless ok.
bData
ts
bData: string;Player b's document after the trade (JSON); empty unless ok.
id
ts
id: number;ok
ts
ok: boolean;reason
ts
reason: string;Empty when ok.
FeatureSpec
The features a game may declare. A key that is false is the same as absent.
Properties
characters?
ts
optional characters?: boolean;The splat character bridge (spawn-character and friends).
multiplayer?
ts
optional multiplayer?: boolean | MultiplayerOptions;Rooms, players and replication.
FirstPersonOptions
Options for camera.firstPerson.
Properties
eyeHeight?
ts
optional eyeHeight?: number;Eye height above the entity origin, metres. Default 1.7.
fovYDeg?
ts
optional fovYDeg?: number;Vertical field of view in degrees. Default 75.
FollowOptions
Options for camera.follow.
Properties
distance?
ts
optional distance?: number;Boom length behind the target, metres. Default 4.
fovYDeg?
ts
optional fovYDeg?: number;Vertical field of view in degrees. Default 60.
height?
ts
optional height?: number;Rig-local height offset, metres. Default 1.6.
pitch?
ts
optional pitch?: number;Orbit pitch in radians; positive looks up. Defaults to the accumulator.
yaw?
ts
optional yaw?: number;Orbit yaw in radians about +Y; 0 puts the camera behind the target.
Defaults to the built-in look accumulator. Pass it when the game keeps its own orbit — a third-person camera usually wants a tighter pitch range and its own sensitivity than the first-person one the accumulator is tuned for.
FrameInput
One fixed simulation step of host state handed to the guest.
Properties
bodies
ts
bodies: ArrayLike<number>;Post-step body transforms, stride 15, sorted ascending by body id.
contacts
ts
contacts: readonly Contact[];dt
ts
dt: number;elapsed
ts
elapsed: number;events
ts
events: readonly GameEvent[];frame
ts
frame: number | bigint;Monotonic fixed-step counter. jco lifts u64 as a bigint on the host and a number in the guest, so both are legal here; the runtime Number()s it once, on the way in.
input
ts
input: InputState;players
ts
players: readonly PlayerInput[];Every player's input in a room, ascending by id. Empty for a single-player game. A held seat (its player dropped) is left out until they are back.
FrameOutput
Everything the guest hands back for one fixed step.
Properties
camera
ts
camera: CameraState;commands
ts
commands: readonly Command[];hud?
ts
optional hud?: string;HUD JSON, present only on the frames it changed.
localCommands
ts
localCommands: readonly Command[];Applied by the authority after commands; never forwarded to a client.
transforms
ts
transforms: Float32Array;Entity transforms, stride 12. Never empty: see the zero-row rule.
GameConfig
The init payload.
Properties
devMode
ts
devMode: boolean;fixedHz
ts
fixedHz: number;options?
ts
optional options?: string;seed
ts
seed: number | bigint;Deterministic run seed. A bigint on the host, a number in the guest; the runtime Number()s it once, on the way in.
viewportHeight
ts
viewportHeight: number;viewportWidth
ts
viewportWidth: number;GameContext
Everything a system can reach.
The same object is handed to every system on every frame — it is mutated in place, never rebuilt, so never retain it or destructure frame outside the call.
Properties
audio
ts
readonly audio: object;Sound playback.
listener()
ts
listener(position, rotation): void;Place the audio listener.
Parameters
| Parameter | Type | Description |
|---|---|---|
position | Vec3 | Listener position. |
rotation | Quat | Listener rotation, xyzw. |
Returns
void
Nothing.
play()
ts
play(asset, options?): number;Start a sound.
Parameters
| Parameter | Type | Description |
|---|---|---|
asset | string | number | Manifest string id or asset handle. |
options? | PlayOptions | Attachment, gain, pitch, looping and bus. |
Returns
number
The guest-minted sound handle, or 0 when the asset is unknown.
stop()
ts
stop(sound, fadeMs?): void;Stop a playing sound.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
sound | number | undefined | A handle from play. |
fadeMs | number | 0 | Fade-out in milliseconds; 0 stops immediately. |
Returns
void
Nothing.
camera
ts
readonly camera: object;The camera record for this frame.
look
Get Signature
ts
get look(): object;Accumulated look angles, in radians. Mutate to snap the view.
Returns
object
The live look state.
pitch
ts
pitch: number;sensitivity
ts
sensitivity: number;yaw
ts
yaw: number;state
Get Signature
ts
get state(): CameraState;The whole camera record, for games that want every knob.
Returns
The live record. Mutate it; do not replace it.
firstPerson()
ts
firstPerson(entity, options?): void;Mount the camera at an entity's eyes.
Yaw and pitch come from the built-in look accumulator, which integrates input.mouse.dx/dy once per tick.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity to mount on. |
options? | FirstPersonOptions | Eye height and field of view. |
Returns
void
Nothing.
follow()
ts
follow(entity, options?): void;Put the camera on a spring arm behind an entity.
The host owns the arm and its collision; the guest only states the intent. position is the orbit pivot and rotation the direction the player is looking, so a host with no rig still ends up somewhere sensible.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity to follow. |
options? | FollowOptions | Boom length, height offset, field of view and orbit angles. |
Returns
void
Nothing.
lookAt()
ts
lookAt(point): void;Aim the camera at a world point, overriding its rotation.
Parameters
| Parameter | Type | Description |
|---|---|---|
point | Vec3 | null | The point to look at, or null to use the rotation again. |
Returns
void
Nothing.
set()
ts
set(
position,
rotation,
fovYDeg?
): void;Place the camera explicitly, detaching it from any entity.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
position | Vec3 | undefined | Eye position. |
rotation | Quat | undefined | Eye rotation, xyzw. |
fovYDeg | number | 60 | Vertical field of view in degrees. Default 60. |
Returns
void
Nothing.
character
ts
readonly character: object;Splat characters.
bundleOf()
ts
bundleOf(entity): number;The character bundle handle attached to an entity, or 0.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity id. |
Returns
number
The bundle asset handle.
lookAt()
ts
lookAt(
entity,
target,
weight?
): void;Aim the head and eyes at a world point.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
entity | number | undefined | Entity with a Character component. |
target | Vec3 | null | undefined | The point, or null to release and return to the idle. |
weight | number | 1 | Blend weight in 0..1. Default 1. |
Returns
void
Nothing.
say()
ts
say(
entity,
text,
voice?,
visemes?
): void;Speak a line.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity with a Character component. |
text | string | Subtitle text; the host decides whether to show it. |
voice? | string | number | Manifest string id or handle of a voice line. |
visemes? | string | Viseme track as JSON, matching the bundle's space. |
Returns
void
Nothing.
setClipWeights()
ts
setClipWeights(
entity,
clips,
weights,
timeScale?
): void;Set explicit per-clip weights on the body layer.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
entity | number | undefined | Entity with a Character component. |
clips | readonly string[] | undefined | Clip names. |
weights | readonly number[] | Float32Array<ArrayBufferLike> | undefined | Positional weights; must be the same length as clips. |
timeScale | number | 1 | Playback rate for the whole layer. Default 1. |
Returns
void
Nothing.
setExpression()
ts
setExpression(
entity,
space,
weights
): void;Set facial expression coefficients.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity with a Character component. |
space | ExpressionSpace | Coordinate space: 52 ARKit, 387 GNM, or the 68-float view. |
weights | readonly number[] | Float32Array<ArrayBufferLike> | Coefficients; length must match the space. |
Returns
void
Nothing.
setState()
ts
setState(
entity,
state,
vx,
vy,
vz,
grounded?
): void;Drive the locomotion state machine.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
entity | number | undefined | Entity with a Character component. |
state | string | undefined | State name, for example `'idle' |
vx | number | undefined | World-space velocity x, drives the locomotion blend. |
vy | number | undefined | World-space velocity y. |
vz | number | undefined | World-space velocity z. |
grounded | boolean | true | Whether the character is on the ground. |
Returns
void
Nothing.
config
ts
readonly config: GameConfig;The init config.
contacts
ts
readonly contacts: readonly Contact[];Contacts reported since the previous tick.
data
ts
readonly data: DataFacade;Player documents and the game's own document in the room's store, and trades between two players. The authority writes; a client's calls do nothing.
dt
ts
readonly dt: number;Fixed timestep in seconds.
elapsed
ts
readonly elapsed: number;Simulated seconds since init.
events
ts
readonly events: readonly GameEvent[];Host-side occurrences since the previous tick, in order.
frame
ts
readonly frame: number;Monotonic fixed-step counter, starting at 0.
hud
ts
readonly hud: object;The HUD model.
clear()
ts
clear(): void;Drop the HUD: emit an empty model this frame.
Idempotent — calling it every frame emits {} once, exactly like set.
Returns
void
Nothing.
invalidate()
ts
invalidate(): void;Forget what the host has seen, so the next set emits even if the model did not change. Use it after the host reloaded its overlay.
On its own it sends nothing: the next set does. To send an empty model now, call clear().
Returns
void
Nothing.
set()
ts
set(model): boolean;Set the HUD model for this frame.
Nested plain objects are compared by value four levels down, so a model rebuilt inline every frame is recognised as unchanged. Arrays and class instances are compared by identity: keep those out of the model, or build them once and mutate nothing.
Parameters
| Parameter | Type | Description |
|---|---|---|
model | Record<string, unknown> | A JSON-serialisable object. |
Returns
boolean
True when the model changed and JSON will cross this frame.
input
ts
readonly input: object;Keyboard, mouse and gamepad.
focused
Get Signature
ts
get focused(): boolean;Does the canvas have focus? Treat input as neutral when it does not.
Returns
boolean
True while the canvas is focused.
mods
Get Signature
ts
get mods(): InputMods;Keyboard modifier state.
Returns
The modifiers for this frame.
mouse
Get Signature
ts
get mouse(): MouseState;Pointer position, per-frame delta, wheel and button bitsets.
Returns
The mouse state for this frame. Owned by the SDK; never retain it.
axis2()
ts
axis2(
negX,
posX,
negY,
posY
): Axis2;A two-axis reading built from four keys.
Parameters
| Parameter | Type | Description |
|---|---|---|
negX | string | Key that drives x negative, for example 'A'. |
posX | string | Key that drives x positive, for example 'D'. |
negY | string | Key that drives y negative, for example 'S'. |
posY | string | Key that drives y positive, for example 'W'. |
Returns
A pooled { x, y } with components in -1..1. Never retain it.
gamepad()
ts
gamepad(index):
| {
axes: ArrayLike<number>;
buttons: number;
connected: boolean;
index: number;
pressed: number;
released: number;
}
| null;One connected gamepad.
Parameters
| Parameter | Type | Description |
|---|---|---|
index | number | Navigator gamepad index. |
Returns
| { axes: ArrayLike<number>; buttons: number; connected: boolean; index: number; pressed: number; released: number; } | null
The gamepad, or null when nothing is connected at that index. Owned by the SDK; never retain it.
isDown()
ts
isDown(key): boolean;Is the key held this frame?
Parameters
| Parameter | Type | Description |
|---|---|---|
key | string | A DOM code ('KeyW'), a bare letter/digit ('W', '1') or an alias ('Shift', 'Esc'). |
Returns
boolean
True while the key is down.
mouseDown()
ts
mouseDown(button): boolean;Is a mouse button held?
Parameters
| Parameter | Type | Description |
|---|---|---|
button | number | A MOUSE_BUTTONS value, or a raw bit mask. |
Returns
boolean
True while the button is down.
mousePressed()
ts
mousePressed(button): boolean;Did a mouse button go down this frame?
Parameters
| Parameter | Type | Description |
|---|---|---|
button | number | A MOUSE_BUTTONS value, or a raw bit mask. |
Returns
boolean
True on the frame the button went down.
mouseReleased()
ts
mouseReleased(button): boolean;Did a mouse button come up this frame?
Parameters
| Parameter | Type | Description |
|---|---|---|
button | number | A MOUSE_BUTTONS value, or a raw bit mask. |
Returns
boolean
True on the frame the button came up.
pressed()
ts
pressed(key): boolean;Did the key go down this frame?
Parameters
| Parameter | Type | Description |
|---|---|---|
key | string | A key name. |
Returns
boolean
True on the frame the key went down.
released()
ts
released(key): boolean;Did the key come up this frame?
Parameters
| Parameter | Type | Description |
|---|---|---|
key | string | A key name. |
Returns
boolean
True on the frame the key came up.
localPlayer
ts
readonly localPlayer: PlayerHandle | null;The player this page belongs to, on a client; null on the authority and in solo.
net
ts
readonly net: NetFacade;Where this guest runs, game messages in and out, and local-only commands.
physics
ts
readonly physics: object;Physics queries and body commands.
ALL_LAYERS
ts
ALL_LAYERS: CollisionLayers;Every collision layer, for queries that should hit anything.
applyImpulse()
ts
applyImpulse(
entity,
x,
y,
z,
atX?,
atY?,
atZ?
): void;Apply a one-shot impulse.
With no application point the impulse acts at the centre of mass. Give one — all three coordinates — to apply it off-centre and impart spin.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity with a body. |
x | number | Impulse x, newton-seconds. |
y | number | Impulse y. |
z | number | Impulse z. |
atX? | number | World-space application point x, or omit for the centre of mass. |
atY? | number | Application point y. |
atZ? | number | Application point z. |
Returns
void
Nothing.
isGrounded()
ts
isGrounded(entity): boolean;Actual walkable ground contact from the last host physics step. No host call. Unknown, steep and unsupported contacts return false, even at a jump apex.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Character entity. |
Returns
boolean
Whether the character is supported by walkable ground.
moveCharacter()
ts
moveCharacter(
entity,
vx,
vy,
vz,
jump?,
crouch?,
maxSlopeDeg?
): void;Drive a character body for this step.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
entity | number | undefined | Entity whose body was created with kind: 'character'. |
vx | number | undefined | Desired world-space velocity x, metres per second. |
vy | number | undefined | Desired world-space velocity y. |
vz | number | undefined | Desired world-space velocity z. |
jump | boolean | false | Request a jump this step. |
crouch | boolean | false | Request a crouch this step. |
maxSlopeDeg | number | 45 | Maximum walkable slope. |
Returns
void
Nothing.
overlapSphere()
ts
overlapSphere(
center,
radius,
maxResults?,
mask?,
ignoreEntity?
): readonly OverlapHit[];Bodies overlapping a sphere, nearest first.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
center | Vec3 | undefined | Sphere centre. |
radius | number | undefined | Sphere radius in metres. |
maxResults | number | 16 | Cap on returned hits. |
mask? | CollisionLayers | undefined | Layers to consider; defaults to every layer. |
ignoreEntity? | number | undefined | Entity to skip. |
Returns
readonly OverlapHit[]
The overlapping bodies.
raycast()
ts
raycast(
origin,
direction,
maxDistance,
mask?,
ignoreEntity?
): RayHit | null;Closest hit along a ray.
Parameters
| Parameter | Type | Description |
|---|---|---|
origin | Vec3 | World-space ray origin. |
direction | Vec3 | Ray direction; need not be normalised. |
maxDistance | number | Maximum distance in metres. |
mask? | CollisionLayers | Layers to consider; defaults to every layer. |
ignoreEntity? | number | Entity to skip, usually the caster. |
Returns
RayHit | null
The hit, or null on a miss. The hit object comes from the host and is freshly allocated: this call is not allocation-free.
raycastBatch()
ts
raycastBatch(rays): readonly (RayHit | null | undefined)[];Many rays in one round trip. Result index i matches rays[i].
Parameters
| Parameter | Type | Description |
|---|---|---|
rays | readonly object[] | The rays. Build them once and mutate them in place. |
Returns
readonly (RayHit | null | undefined)[]
One result per ray; undefined or null entries are misses.
setEnabled()
ts
setEnabled(entity, enabled): void;Enable or disable a body in the broad phase.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity with a body. |
enabled | boolean | Whether the body participates. |
Returns
void
Nothing.
setVelocity()
ts
setVelocity(
entity,
x,
y,
z,
ax?,
ay?,
az?
): void;Overwrite a body's velocity.
Angular velocity is left alone unless all three angular components are given.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity with a body. |
x | number | Linear velocity x. |
y | number | Linear velocity y. |
z | number | Linear velocity z. |
ax? | number | Angular velocity x, radians per second. |
ay? | number | Angular velocity y. |
az? | number | Angular velocity z. |
Returns
void
Nothing.
teleport()
ts
teleport(
entity,
x,
y,
z
): void;Teleport a body, clearing its velocities.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity with a body. |
x | number | Position x. |
y | number | Position y. |
z | number | Position z. |
Returns
void
Nothing.
player
ts
readonly player: number;The entity the declarative player block spawned in a single-player game, or 0. In a room each player has their own: playerEntity(id).
players
ts
readonly players: Players;The room's players by id: everyone joined and not yet left, plus anyone whose input is in this step's frame-input.players. Empty for a single-player game. Rebuilt only when that set changes, never per tick. Also says who the host is (host) and lists the players in id order (list), which a system walks without allocating.
rng
ts
readonly rng: Rng;The seeded generator. Never Math.random.
rules
ts
readonly rules: Readonly<Record<string, unknown>>;The rules object from defineGame, verbatim.
world
ts
readonly world: object;The bitecs world, for query, addComponent and friends.
Methods
assetId()
ts
assetId(name): number;Resolve a manifest string id to a handle, cached.
Parameters
| Parameter | Type |
|---|---|
name | string |
Returns
number
despawn()
ts
despawn(entity): void;Destroy an entity and its body.
Parameters
| Parameter | Type |
|---|---|
entity | number |
Returns
void
playerEntity()
ts
playerEntity(id): number;The entity definition.player spawned for a room player, or 0.
Parameters
| Parameter | Type |
|---|---|
id | number |
Returns
number
spawn()
ts
spawn(
def,
position,
rotation?
): number;Instantiate a prefab.
Parameters
| Parameter | Type |
|---|---|
def | PrefabDef |
position | Vec3 |
rotation? | Quat |
Returns
number
GameDataEvent
The room's saved game-wide document as JSON.
Properties
data
ts
data: string;GameError
The error payload of init and restore.
Properties
code
ts
code: ErrorCode;message
ts
message: string;GamepadState
One gamepad in the standard mapping.
Properties
axes
ts
axes: ArrayLike<number>;buttons
ts
buttons: number;connected
ts
connected: boolean;index
ts
index: number;pressed
ts
pressed: number;released
ts
released: number;GameSpec
Everything a game declares.
Properties
assets?
ts
optional assets?: readonly string[];Manifest string ids to resolve during init and cache.
features?
ts
optional features?: FeatureSpec;Optional engine modules this game needs. See featuresOf.
init?
ts
optional init?: (ctx) => void;Extra setup, run after the declarative spawns.
Parameters
| Parameter | Type |
|---|---|
ctx | GameContext |
Returns
void
player?
ts
optional player?: PlayerSpec;The player and its camera. Spawned once at init in a single-player game; on the authority of a room, once per joined player instead.
restore?
ts
optional restore?: (state) => void;Read back what snapshot returned.
Parameters
| Parameter | Type |
|---|---|
state | unknown |
Returns
void
rules?
ts
optional rules?: Record<string, unknown>;Arbitrary tuning values, handed back as ctx.rules.
shutdown?
ts
optional shutdown?: (ctx) => void;Called once before the guest is torn down.
Parameters
| Parameter | Type |
|---|---|
ctx | GameContext |
Returns
void
snapshot?
ts
optional snapshot?: () => unknown;Extra state to fold into snapshot(). Must be JSON-serialisable.
Returns
unknown
spawns?
ts
optional spawns?: readonly SpawnSpec[];Entities to create during init.
systems?
ts
optional systems?: readonly (SidedSystem | System)[];User systems, run in order after the built-ins. A bare function runs on the authority once features.multiplayer is declared, everywhere otherwise; { run, on } says where explicitly.
update?
ts
optional update?: (ctx) => void;Convenience: one more system, run after systems.
Parameters
| Parameter | Type |
|---|---|
ctx | GameContext |
Returns
void
world?
ts
optional world?: WorldSpec;World settings.
Guest
A guest instance: the five WIT exports plus a liveness flag.
Extends
Properties
dead
ts
readonly dead: boolean;True once the runtime has given up on user code.
state
ts
readonly state: RuntimeState;The runtime, for tests and tooling.
Methods
init()
ts
init(config): void;Parameters
| Parameter | Type |
|---|---|
config | GameConfig |
Returns
void
Inherited from
restore()
ts
restore(state): void;Parameters
| Parameter | Type |
|---|---|
state | ArrayLike<number> |
Returns
void
Inherited from
shutdown()
ts
shutdown(): void;Returns
void
Inherited from
snapshot()
ts
snapshot(): Uint8Array;Returns
Uint8Array
Inherited from
tick()
ts
tick(input): FrameOutput;Parameters
| Parameter | Type |
|---|---|
input | FrameInput |
Returns
Inherited from
GuestExports
The five functions the WIT game interface exports.
Extended by
Methods
init()
ts
init(config): void;Parameters
| Parameter | Type |
|---|---|
config | GameConfig |
Returns
void
restore()
ts
restore(state): void;Parameters
| Parameter | Type |
|---|---|
state | ArrayLike<number> |
Returns
void
shutdown()
ts
shutdown(): void;Returns
void
snapshot()
ts
snapshot(): Uint8Array;Returns
Uint8Array
tick()
ts
tick(input): FrameOutput;Parameters
| Parameter | Type |
|---|---|
input | FrameInput |
Returns
HealthStore
Current and maximum hit points.
Properties
current
ts
current: Float32Array;max
ts
max: Float32Array;HostApi
The host services a guest may call, in guest-side JS shapes.
This is the SDK's own narrow view of the three gameable:engine import interfaces. packages/sdk/src/wit/entry.ts adapts the real WIT imports to it; gameable/test implements it directly for node tests.
Extended by
Methods
describe()
ts
describe(id): AssetDesc | null | undefined;Metadata for a handle, or nullish when the handle is unknown.
Parameters
| Parameter | Type |
|---|---|
id | number |
Returns
AssetDesc | null | undefined
log()
ts
log(level, msg): void;Route a message to the host logger.
Parameters
| Parameter | Type |
|---|---|
level | LogLevel |
msg | string |
Returns
void
nowMs()
ts
nowMs(): number;Monotonic milliseconds since engine start. Never feed this to simulation.
Returns
number
overlapSphere()
ts
overlapSphere(
center,
radius,
filter,
maxResults
): readonly OverlapHit[];Bodies overlapping a sphere, nearest first.
Parameters
| Parameter | Type |
|---|---|
center | Vec3 |
radius | number |
filter | QueryFilter |
maxResults | number |
Returns
readonly OverlapHit[]
raycast()
ts
raycast(
origin,
direction,
maxDistance,
filter
): RayHit | null | undefined;Closest hit along a ray, or nullish on a miss.
Parameters
| Parameter | Type |
|---|---|
origin | Vec3 |
direction | Vec3 |
maxDistance | number |
filter | QueryFilter |
Returns
RayHit | null | undefined
raycastBatch()
ts
raycastBatch(rays): readonly (RayHit | null | undefined)[];One round trip for many rays; result index i matches rays[i].
Parameters
| Parameter | Type |
|---|---|
rays | readonly RayQuery[] |
Returns
readonly (RayHit | null | undefined)[]
resolveId()
ts
resolveId(name): number | null | undefined;Manifest string id to handle, or nullish when the manifest has no entry.
Parameters
| Parameter | Type |
|---|---|
name | string |
Returns
number | null | undefined
seed()
ts
seed(): number;The deterministic run seed, as a number (already Number()-coerced).
Returns
number
HostFrameInput
frame-input in host-side shapes.
jco lifts u64 to bigint on the host and to number in the guest, and the host is free to hand over real typed arrays. Everything else is identical, which is why the guest types accept ArrayLike<number>.
Properties
bodies
ts
bodies: Float32Array;contacts
ts
contacts: readonly Contact[];dt
ts
dt: number;elapsed
ts
elapsed: number;events
ts
events: readonly GameEvent[];frame
ts
frame: bigint;input
ts
input: InputState;players
ts
players: readonly PlayerInput[];HostGameConfig
game-config in host-side shapes: seed is a bigint.
Properties
devMode
ts
devMode: boolean;fixedHz
ts
fixedHz: number;options?
ts
optional options?: string;seed
ts
seed: bigint;viewportHeight
ts
viewportHeight: number;viewportWidth
ts
viewportWidth: number;HudState
HUD change detection state.
Properties
last
ts
last: Record<string, unknown> | null;Shallow copy of the last model the game set, or null before the first.
pending
ts
pending: string | undefined;JSON to emit this frame, or undefined when the model did not change.
InputMods
Keyboard modifier state. Every key is present on input.
Properties
alt
ts
alt: boolean;capsLock
ts
capsLock: boolean;ctrl
ts
ctrl: boolean;meta
ts
meta: boolean;numLock
ts
numLock: boolean;shift
ts
shift: boolean;InputState
Everything the host knows about input for one fixed step.
Extended by
Properties
focused
ts
focused: boolean;gamepads
ts
gamepads: readonly GamepadState[];keys
ts
keys: KeyState;mods
ts
mods: InputMods;mouse
ts
mouse: MouseState;KeyState
256 key codes packed into 8 u32 words, one list per edge.
Properties
down
ts
down: ArrayLike<number>;pressed
ts
pressed: ArrayLike<number>;released
ts
released: ArrayLike<number>;LoadAssetCmd
Ask the host to start loading an asset.
Properties
asset
ts
asset: number;priority
ts
priority: number;LookAtCmd
Aim a character's head and eyes at a world point.
Properties
entity
ts
entity: number;target?
ts
optional target?: Vec3;weight
ts
weight: number;LookState
First-person / third-person look accumulator owned by the built-in camera.
Properties
pitch
ts
pitch: number;sensitivity
ts
sensitivity: number;yaw
ts
yaw: number;MessageOptions
Options for defineMessage.
Example
ts
const Chat = defineMessage('chat', (p): p is string => typeof p === 'string', { maxBytes: 256 });Properties
maxBytes?
ts
optional maxBytes?: number;The largest payload, in UTF-8 bytes of its JSON; 1 to 2,048. Default 2,048, the wire cap.
MouseState
Pointer position, deltas, wheel and button edges for one frame.
Properties
buttons
ts
buttons: number;dx
ts
dx: number;dy
ts
dy: number;locked
ts
locked: boolean;pressed
ts
pressed: number;released
ts
released: number;wheel
ts
wheel: number;x
ts
x: number;y
ts
y: number;MoveCharacterCmd
Drive a character body for one step.
Properties
body
ts
body: number;crouch
ts
crouch: boolean;desiredVelocity
ts
desiredVelocity: Vec3;jump
ts
jump: boolean;maxSlopeDeg
ts
maxSlopeDeg: number;MultiplayerOptions
Options for the multiplayer feature.
Properties
maxPlayers?
ts
optional maxPlayers?: number;Seats in a room, ids 0..maxPlayers - 1. Default 8. roomSeats reads it, for the guest's player slots and for the room's door alike.
predict?
ts
optional predict?: boolean;Predict each page's own character body: the page steps its own Jolt world (the level's static colliders and the body its client guest adds for its player) so the player moves on key-down, and the authority's rows correct it. Default false. See the multiplayer concept page.
sendHz?
ts
optional sendHz?: number;Transform rows per second sent to each player. Default 20.
MutableGamepad
A gamepad snapshot the SDK owns and reuses every frame.
Properties
axes
ts
axes: Float32Array;Standard mapping: lx, ly, rx, ry, left trigger, right trigger.
buttons
ts
buttons: number;connected
ts
connected: boolean;index
ts
index: number;pressed
ts
pressed: number;released
ts
released: number;NetFacade
The ctx.net facade, one per guest.
Example
ts
function votes(ctx: GameContext): void {
if (!ctx.net.isAuthority) return;
for (const vote of ctx.net.messages(Vote)) {
ctx.net.send('voted', { by: vote.player, for: vote.payload.for });
}
ctx.net.local(() => ctx.audio.play('tick')); // never sent to a player
}Properties
stats
ts
readonly stats: NetStats;Counters since init.
Accessors
isAuthority
Get Signature
ts
get isAuthority(): boolean;Returns
boolean
True on the authority and in a single-player game.
localPlayer
Get Signature
ts
get localPlayer(): number;Returns
number
The player this page belongs to on a client; 0 elsewhere.
role
Get Signature
ts
get role(): NetRole;Returns
Where this guest runs.
Methods
beginTick()
ts
beginTick(): void;Start a tick: this tick's messages are read afresh. Runtime only.
Returns
void
local()
ts
local(fn): void;Run fn with every command it queues routed to frame-output.local-commands: applied where this guest runs, never sent to a player. Nested calls stay local; a throw still restores the network buffer. Allocates nothing.
Parameters
| Parameter | Type | Description |
|---|---|---|
fn | () => void | The code whose commands stay local. |
Returns
void
messages()
ts
messages<T>(message): readonly NetMessage<T>[];This tick's validated messages for one definition, in arrival order.
A payload over the definition's maxBytes, not JSON, or failing its check is dropped and counted in stats.dropped, never thrown at a system; the rest count in stats.received. The list and its entries are pooled and refilled each tick: read them inside the tick, never retain them. Reading the same name twice in a tick counts nothing twice.
A bare name (deprecated) reads the same list without a check, unless the name's definition has been read before in this run; it caps at 2,048 bytes.
Type Parameters
| Type Parameter |
|---|
T |
Parameters
| Parameter | Type | Description |
|---|---|---|
message | string | MessageDef<T> | The definition, or a bare message name. |
Returns
readonly NetMessage<T>[]
The messages, { player, payload }.
reset()
ts
reset(): void;Forget the counters and every cached message list. init only.
Returns
void
send()
Call Signature
ts
send<T>(
message,
payload,
options?
): void;Send a game message: from the authority to one player or all of them, from a client up to the authority (to is ignored there).
Pass a defineMessage definition to check the payload with its guard and cap it at its maxBytes before it leaves; a bare name (deprecated) checks only the 2,048-byte wire cap.
The payload is serialised with JSON.stringify, so a tick that sends allocates that one string; a tick that sends nothing allocates nothing. undefined sends null.
Type Parameters
| Type Parameter |
|---|
T |
Parameters
| Parameter | Type | Description |
|---|---|---|
message | MessageDef<T> | The definition, or a bare message name. |
payload | T | Anything JSON-serialisable; at most maxBytes (2,048) as UTF-8 JSON. |
options? | SendOptions | to one player, and reliable (default true). A payload that does not serialise (a function or a symbol), fails the definition's check, or whose JSON is over the cap sends nothing and counts in stats.unsent; it never throws, since a throwing system fails the tick. |
Returns
void
Call Signature
ts
send(
name,
payload,
options?
): void;Send a game message: from the authority to one player or all of them, from a client up to the authority (to is ignored there).
Pass a defineMessage definition to check the payload with its guard and cap it at its maxBytes before it leaves; a bare name (deprecated) checks only the 2,048-byte wire cap.
The payload is serialised with JSON.stringify, so a tick that sends allocates that one string; a tick that sends nothing allocates nothing. undefined sends null.
Parameters
| Parameter | Type | Description |
|---|---|---|
name | string | - |
payload | unknown | Anything JSON-serialisable; at most maxBytes (2,048) as UTF-8 JSON. |
options? | SendOptions | to one player, and reliable (default true). A payload that does not serialise (a function or a symbol), fails the definition's check, or whose JSON is over the cap sends nothing and counts in stats.unsent; it never throws, since a throwing system fails the tick. |
Returns
void
setPhase()
ts
setPhase(word): void;Say what the room is doing, in a word (lobby, playing, voting), for the public room list beside its code and seats. Authority only: on a client, and in a game with no room, it does nothing. The same word again sends nothing and allocates nothing; a new one sends the reserved message aos:phase, which the room server reads and never forwards to a player.
Parameters
| Parameter | Type | Description |
|---|---|---|
word | string | 1 to 32 ASCII letters, digits or dashes. Anything else sends nothing, counts in stats.unsent and is logged once; it never throws. |
Returns
void
Example
ts
function lobby(ctx: GameContext): void {
ctx.net.setPhase(started ? 'playing' : 'lobby');
}NetMessage
What messages hands back: one player's message, its payload parsed.
Example
ts
for (const m of ctx.net.messages(Vote)) tally(m.player, m.payload.for);Type Parameters
| Type Parameter |
|---|
T |
Properties
payload
ts
readonly payload: T;The JSON payload, parsed.
player
ts
readonly player: number;The sender.
NetMessageEvent
A game message a player sent, delivered to the authority on its next tick.
Named NetMessageEvent rather than the WIT's message-event so it never shadows the DOM's MessageEvent in a page that imports both.
Properties
name
ts
name: string;payload
ts
payload: string;JSON.
player
ts
player: number;The sender.
NetStats
Counters a game or the debug overlay can read.
Example
ts
ctx.hud.set({ dropped: ctx.net.stats.dropped, received: ctx.net.stats.received });Properties
dropped
ts
dropped: number;Messages read and dropped: over their maxBytes, not JSON, or failing their defineMessage check. A payload over 2,048 bytes is dropped by the room before it reaches the guest, and is not counted here.
received
ts
received: number;Messages read and handed to a system.
unsent
ts
unsent: number;ctx.net.send calls since init that sent nothing: the payload failed its check, did not serialise, or was over the cap. Never thrown, because a throw fails the tick and enough of them kill the guest for the room.
OverlapHit
One body overlapping a query volume.
Properties
body
ts
body: number;depth
ts
depth: number;entity
ts
entity: number;point
ts
point: Vec3;PlayerHandle
One player: who they are, the entity definition.player spawned for them, and facades that read their input and write their own camera and HUD.
Example
ts
for (const [id, p] of ctx.players) {
if (p.input.pressed('Space')) jump(p.entity);
p.hud.set({ name: p.name, id });
}Properties
camera
ts
readonly camera: object;This player's camera: every write queues one set-player-camera per tick.
look
Get Signature
ts
get look(): object;Accumulated look angles, in radians. Mutate to snap the view.
Returns
object
The live look state.
pitch
ts
pitch: number;sensitivity
ts
sensitivity: number;yaw
ts
yaw: number;state
Get Signature
ts
get state(): CameraState;The whole camera record, for games that want every knob.
Returns
The live record. Mutate it; do not replace it.
firstPerson()
ts
firstPerson(entity, options?): void;Mount the camera at an entity's eyes.
Yaw and pitch come from the built-in look accumulator, which integrates input.mouse.dx/dy once per tick.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity to mount on. |
options? | FirstPersonOptions | Eye height and field of view. |
Returns
void
Nothing.
follow()
ts
follow(entity, options?): void;Put the camera on a spring arm behind an entity.
The host owns the arm and its collision; the guest only states the intent. position is the orbit pivot and rotation the direction the player is looking, so a host with no rig still ends up somewhere sensible.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity to follow. |
options? | FollowOptions | Boom length, height offset, field of view and orbit angles. |
Returns
void
Nothing.
lookAt()
ts
lookAt(point): void;Aim the camera at a world point, overriding its rotation.
Parameters
| Parameter | Type | Description |
|---|---|---|
point | Vec3 | null | The point to look at, or null to use the rotation again. |
Returns
void
Nothing.
set()
ts
set(
position,
rotation,
fovYDeg?
): void;Place the camera explicitly, detaching it from any entity.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
position | Vec3 | undefined | Eye position. |
rotation | Quat | undefined | Eye rotation, xyzw. |
fovYDeg | number | 60 | Vertical field of view in degrees. Default 60. |
Returns
void
Nothing.
hud
ts
readonly hud: object;This player's HUD: a changed model queues a set-player-hud.
clear()
ts
clear(): void;Drop the HUD: emit an empty model this frame.
Idempotent — calling it every frame emits {} once, exactly like set.
Returns
void
Nothing.
invalidate()
ts
invalidate(): void;Forget what the host has seen, so the next set emits even if the model did not change. Use it after the host reloaded its overlay.
On its own it sends nothing: the next set does. To send an empty model now, call clear().
Returns
void
Nothing.
set()
ts
set(model): boolean;Set the HUD model for this frame.
Nested plain objects are compared by value four levels down, so a model rebuilt inline every frame is recognised as unchanged. Arrays and class instances are compared by identity: keep those out of the model, or build them once and mutate nothing.
Parameters
| Parameter | Type | Description |
|---|---|---|
model | Record<string, unknown> | A JSON-serialisable object. |
Returns
boolean
True when the model changed and JSON will cross this frame.
id
ts
readonly id: number;The player id this slot belongs to.
input
ts
readonly input: object;This player's keys, mouse and gamepads, read like input.
focused
Get Signature
ts
get focused(): boolean;Does the canvas have focus? Treat input as neutral when it does not.
Returns
boolean
True while the canvas is focused.
mods
Get Signature
ts
get mods(): InputMods;Keyboard modifier state.
Returns
The modifiers for this frame.
mouse
Get Signature
ts
get mouse(): MouseState;Pointer position, per-frame delta, wheel and button bitsets.
Returns
The mouse state for this frame. Owned by the SDK; never retain it.
axis2()
ts
axis2(
negX,
posX,
negY,
posY
): Axis2;A two-axis reading built from four keys.
Parameters
| Parameter | Type | Description |
|---|---|---|
negX | string | Key that drives x negative, for example 'A'. |
posX | string | Key that drives x positive, for example 'D'. |
negY | string | Key that drives y negative, for example 'S'. |
posY | string | Key that drives y positive, for example 'W'. |
Returns
A pooled { x, y } with components in -1..1. Never retain it.
gamepad()
ts
gamepad(index):
| {
axes: ArrayLike<number>;
buttons: number;
connected: boolean;
index: number;
pressed: number;
released: number;
}
| null;One connected gamepad.
Parameters
| Parameter | Type | Description |
|---|---|---|
index | number | Navigator gamepad index. |
Returns
| { axes: ArrayLike<number>; buttons: number; connected: boolean; index: number; pressed: number; released: number; } | null
The gamepad, or null when nothing is connected at that index. Owned by the SDK; never retain it.
isDown()
ts
isDown(key): boolean;Is the key held this frame?
Parameters
| Parameter | Type | Description |
|---|---|---|
key | string | A DOM code ('KeyW'), a bare letter/digit ('W', '1') or an alias ('Shift', 'Esc'). |
Returns
boolean
True while the key is down.
mouseDown()
ts
mouseDown(button): boolean;Is a mouse button held?
Parameters
| Parameter | Type | Description |
|---|---|---|
button | number | A MOUSE_BUTTONS value, or a raw bit mask. |
Returns
boolean
True while the button is down.
mousePressed()
ts
mousePressed(button): boolean;Did a mouse button go down this frame?
Parameters
| Parameter | Type | Description |
|---|---|---|
button | number | A MOUSE_BUTTONS value, or a raw bit mask. |
Returns
boolean
True on the frame the button went down.
mouseReleased()
ts
mouseReleased(button): boolean;Did a mouse button come up this frame?
Parameters
| Parameter | Type | Description |
|---|---|---|
button | number | A MOUSE_BUTTONS value, or a raw bit mask. |
Returns
boolean
True on the frame the button came up.
pressed()
ts
pressed(key): boolean;Did the key go down this frame?
Parameters
| Parameter | Type | Description |
|---|---|---|
key | string | A key name. |
Returns
boolean
True on the frame the key went down.
released()
ts
released(key): boolean;Did the key come up this frame?
Parameters
| Parameter | Type | Description |
|---|---|---|
key | string | A key name. |
Returns
boolean
True on the frame the key came up.
lanes
ts
readonly lanes: InputLanes;The decoded input this handle's input reads. Internal.
present
ts
present: boolean = false;In this step's frame-input.players. Internal.
seq
ts
seq: number = 0;The input sequence number this step consumed.
Accessors
connected
Get Signature
ts
get connected(): boolean;Returns
boolean
True between this player's player-joined and player-left.
data
Get Signature
ts
get data(): unknown;Returns
unknown
The player's document: the one the room loaded at join, then whatever ctx.data.save or a finished exchange made it; null for none.
entity
Get Signature
ts
get entity(): number;Returns
number
The entity this player controls: the one definition.player spawned at join, or the last one possessed; 0 for none (also after that entity is despawned).
isHost
Get Signature
ts
get isHost(): boolean;Returns
boolean
True for the host (ctx.players.host): the first joiner, until they leave; then the lowest seat still joined.
joinedAt
Get Signature
ts
get joinedAt(): number | null;Returns
number | null
The server's clock when the room loaded this player's document, in ms since the epoch, or null when the room has no store. The guest has no clock of its own (now-ms counts from engine start), so this is the one wall time it gets: add ctx.elapsed since the join to it.
name
Get Signature
ts
get name(): string;Returns
string
The display name the room gave at join, or ''.
savedAt
Get Signature
ts
get savedAt(): number | null;Returns
number | null
When the store last wrote the document handed over at join, in ms since the epoch by the server's clock, or null when there was none. Offline time is joinedAt - savedAt.
Methods
forgetEntity()
ts
forgetEntity(entity): void;The possessed entity was despawned: the player controls none. The host clears its own mapping on the despawn, so nothing is sent.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | A despawned entity. |
Returns
void
integrateLook()
ts
integrateLook(): void;Integrate this step's mouse movement into the look angles, as the built-in accumulator does for the local player. Allocates nothing.
Returns
void
join()
ts
join(name, data): void;The player took this seat. Parses the saved document: a join-tick allocation.
Parameters
| Parameter | Type | Description |
|---|---|---|
name | string | The display name. |
data | string | null | undefined | The room's { doc, savedAt, now } as JSON, when it has a store (a bare document is taken as the doc). |
Returns
void
leave()
ts
leave(): void;The player left; the caller has already despawned PlayerHandle.entity.
Returns
void
possess()
ts
possess(entity): void;Make this player control another entity: a respawn, a vehicle, a class swap. On the authority it queues a set-player-entity, so the room follows the player there (relevancy) and their client learns which entity is theirs. Despawning the entity later leaves the player with none until the next possess.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | The entity, or 0 to control none. |
Returns
void
Example
ts
const body = ctx.spawn(Avatar, spawnPoint);
ctx.players.get(id)?.possess(body);restoreSeat()
ts
restoreSeat(
connected,
name,
entity,
data
): void;Overwrite who holds this seat from a snapshot. The caller rebuilds the map.
Parameters
| Parameter | Type | Description |
|---|---|---|
connected | boolean | Joined and not left. |
name | string | The display name. |
entity | number | The player's entity in the restored world, or 0. |
data | unknown | The saved document, already parsed. |
Returns
void
setEntity()
ts
setEntity(entity): void;The authority spawned this player's entity at join: possess it (which tells the host). Nothing is sent for 0 (a game with no player prefab).
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | The entity spawned for this player, or 0. |
Returns
void
PlayerInput
One player's input for a step, as frame-input.players carries it.
Properties
input
ts
input: InputState;That player's keys, mouse and gamepads.
player
ts
player: number;The player id. 0 is the single-player player, which travels as frame-input.input.
seq
ts
seq: number;The client's input sequence number this step consumed; 0 when unknown.
PlayerJoinedEvent
A player entered the room.
Properties
data?
ts
optional data?: string;The player's saved document as JSON, when the room has a store. Absent otherwise (null in a wasm guest, as every incoming option is).
name
ts
name: string;player
ts
player: number;PlayerLeftEvent
A player left the room.
Properties
player
ts
player: number;reason
ts
reason: string;Why: "left", "timeout", "kicked", ...
Players
The room's players: a read-only Map from id to PlayerHandle, plus who the host is and a list to walk without allocating.
A for...of over the map (or keys(), values(), entries()) makes an iterator, and an entry pair per player, every call: fine in a join handler, not in a system that runs every tick. Walk list with an index there.
Example
ts
const players = ctx.players;
for (let i = 0; i < players.list.length; i += 1) {
const p = players.list[i];
if (p.isHost && p.input.pressed('Enter')) startRound();
}
const host = players.host === undefined ? undefined : players.get(players.host);Extends
ReadonlyMap<number,PlayerHandle>
Properties
host
ts
readonly host: number | undefined;The host's seat id, or undefined while nobody is joined. The first joiner is the host and stays host until they leave (player-left); only then does it pass to the lowest seat still joined. A newcomer who takes a lower, freed seat does not become host. Snapshots keep it.
A host who drops counts as gone for this alone: while the room holds their seat (they are still joined, but out of the frame's players list), the role passes to the lowest seat in that list, and it stays there when they come back. If nobody else is in the list, the held host keeps it.
list
ts
readonly list: readonly PlayerHandle[];The same players as the map, in id order. The same array for the whole run, updated in place on a join or a leave; never per tick. Read it, do not keep a copy of its contents across ticks.
PlayerSpec
The declarative player block.
Properties
camera?
ts
optional camera?: "firstPerson" | "thirdPerson";Which built-in camera rig to drive.
distance?
ts
optional distance?: number;Third-person boom length, metres. Default 4.
eyeHeight?
ts
optional eyeHeight?: number;First-person eye height, metres. Default 1.7.
height?
ts
optional height?: number;Third-person height offset, metres. Default 1.6.
prefab?
ts
optional prefab?: PrefabDef;Prefab to spawn for the player.
sensitivity?
ts
optional sensitivity?: number;Radians of look per pixel of mouse movement. Default 0.0025.
spawn?
ts
optional spawn?: PlayerSpawn;Where each seat spawns; see PlayerSpawn. Default [0, 0, 0].
PlayOptions
Options for audio.play.
Properties
bus?
ts
optional bus?: AudioBus;Mixer bus. Default 'sfx'.
entity?
ts
optional entity?: number;Attach to an entity for positional audio that follows it.
looping?
ts
optional looping?: boolean;Loop until stopped. Default false.
pitch?
ts
optional pitch?: number;Playback-rate multiplier. Default 1.
volume?
ts
optional volume?: number;Linear gain in 0..1. Default 1.
PlaySoundCmd
Start a sound.
Properties
asset
ts
asset: number;bus
ts
bus: AudioBus;entity?
ts
optional entity?: number;looping
ts
looping: boolean;pitch
ts
pitch: number;position?
ts
optional position?: Vec3;sound
ts
sound: number;volume
ts
volume: number;PrefabDef
A prefab, ready to spawn.
Extends
Properties
asset?
ts
optional asset?: string | number;Renderable asset: a manifest string id, or a handle.
Inherited from
body?
ts
optional body?: BodySpec;Physics body, if any.
Inherited from
character?
ts
optional character?: string | number;Splat character bundle: a manifest string id, or a handle.
Inherited from
components?
ts
optional components?: readonly object[];Extra bitecs components and tags to add on spawn.
Inherited from
health?
ts
optional health?: number;Starting hit points; adds the Health component when present.
Inherited from
name?
ts
optional name?: string;Debug label shown in the engine overlay.
Inherited from
prefabId
ts
readonly prefabId: number;Stable index, assigned in declaration order.
scale?
ts
optional scale?: readonly number[];Uniform or per-axis scale. Default [1, 1, 1].
Inherited from
PrefabSpec
Everything a prefab can declare.
Extended by
Properties
asset?
ts
optional asset?: string | number;Renderable asset: a manifest string id, or a handle.
body?
ts
optional body?: BodySpec;Physics body, if any.
character?
ts
optional character?: string | number;Splat character bundle: a manifest string id, or a handle.
components?
ts
optional components?: readonly object[];Extra bitecs components and tags to add on spawn.
health?
ts
optional health?: number;Starting hit points; adds the Health component when present.
name?
ts
optional name?: string;Debug label shown in the engine overlay.
scale?
ts
optional scale?: readonly number[];Uniform or per-axis scale. Default [1, 1, 1].
Quat
A unit quaternion in xyzw order (three.js / Jolt order).
Properties
w
ts
w: number;x
ts
x: number;y
ts
y: number;z
ts
z: number;QueryFilter
Which bodies a physics query considers.
Properties
excludeBody?
ts
optional excludeBody?: number;excludeEntity?
ts
optional excludeEntity?: number;layers
ts
layers: CollisionLayers;solidOnly
ts
solidOnly: boolean;RayHit
Closest hit along a ray.
Properties
body
ts
body: number;distance
ts
distance: number;entity
ts
entity: number;normal
ts
normal: Vec3;point
ts
point: Vec3;RayQuery
One ray in a raycastBatch call.
Properties
direction
ts
direction: Vec3;filter
ts
filter: QueryFilter;maxDistance
ts
maxDistance: number;origin
ts
origin: Vec3;RenderableStore
Renderable asset handle plus host-side flags.
Properties
asset
ts
asset: Uint32Array;Manifest asset handle; 0 means "no renderable".
dirty
ts
dirty: Uint8Array;Non-zero when the host has not yet seen the current value.
flags
ts
flags: Uint32Array;Reserved host flags bitset.
Rgba
Linear RGBA, components in 0..1.
Properties
a
ts
a: number;b
ts
b: number;g
ts
g: number;r
ts
r: number;RigidBodyStore
Rigid body handle and classification.
Properties
dirty
ts
dirty: Uint8Array;Non-zero when the host has not yet seen the current value.
groundState
ts
groundState: Uint8Array;Post-step contact: 0 unknown, 1 ground, 2 steep, 3 unsupported, 4 air.
handle
ts
handle: Uint32Array;Guest-minted body id; 0 means "no body".
kind
ts
kind: Uint8Array;BODY_KIND index.
shape
ts
shape: Uint8Array;SHAPE_KIND index.
Rng
A seeded, serialisable random source.
Methods
float()
ts
float(): number;Next float in [0, 1).
Returns
number
int()
ts
int(n): number;Next integer in [0, n). Returns 0 when n <= 0.
Parameters
| Parameter | Type |
|---|---|
n | number |
Returns
number
load()
ts
load(state): void;Overwrite the state.
Parameters
| Parameter | Type |
|---|---|
state | RngState |
Returns
void
pick()
ts
pick<T>(items): T | undefined;A uniformly chosen element, or undefined when the array is empty.
Type Parameters
| Type Parameter |
|---|
T |
Parameters
| Parameter | Type |
|---|---|
items | ArrayLike<T> |
Returns
T | undefined
range()
ts
range(min, max): number;Next float in [min, max).
Parameters
| Parameter | Type |
|---|---|
min | number |
max | number |
Returns
number
save()
ts
save(out?): RngState;Copy the state out, into out when given.
Parameters
| Parameter | Type |
|---|---|
out? | RngState |
Returns
seed()
ts
seed(value): void;Re-seed from a 32-bit integer.
Parameters
| Parameter | Type |
|---|---|
value | number |
Returns
void
uint32()
ts
uint32(): number;Next u32.
Returns
number
RngState
Four-word xoshiro128** state, as it appears in a snapshot.
Properties
s0
ts
s0: number;s1
ts
s1: number;s2
ts
s2: number;s3
ts
s3: number;RuntimeState
Everything one guest instance owns.
Properties
assetDescs
ts
assetDescs: Map<number, AssetDesc | null>;Asset handle to its description, cached because the manifest is immutable for the life of a run. Per runtime, never module scope: a parity run has a direct guest and a wasm guest, with different hosts, in one realm.
assetIds
ts
assetIds: Map<string, number>;Manifest string id to asset handle, resolved once and cached.
bodyIndex
ts
bodyIndex: BodyIndex;camera
ts
camera: CameraState;carryCommands
ts
carryCommands: boolean;True when the command buffer holds commands built outside tick — the spawns init queued. The next tick keeps them instead of resetting.
commands
ts
commands: NetCommandBuffer;Where the facades queue commands: the network buffer behind frame-output.commands, or localCommands inside ctx.net.local.
config
ts
config: GameConfig;contacts
ts
contacts: readonly Contact[];dead
ts
dead: boolean;Set after repeated user-code failures; the host must rebuild the sandbox.
dt
ts
dt: number;elapsed
ts
elapsed: number;events
ts
events: readonly GameEvent[];failures
ts
failures: number;Consecutive failed ticks.
focused
ts
focused: boolean;frame
ts
frame: number;gamepadCount
ts
gamepadCount: number;gamepads
ts
gamepads: MutableGamepad[];host
ts
host: HostApi;hud
ts
hud: HudState;initialised
ts
initialised: boolean;True once init has completed. Asset resolution warns after this.
inputLanes
ts
inputLanes: InputLanes;The lanes the singleton input reads: this runtime's own (above) in a single-player game and on the authority, the local player's slot on a client.
keysDown
ts
keysDown: Uint32Array;keysPressed
ts
keysPressed: Uint32Array;keysReleased
ts
keysReleased: Uint32Array;localCommands
ts
localCommands: NetCommandBuffer;frame-output.local-commands: applied by the authority, never forwarded.
look
ts
look: LookState;mods
ts
mods: InputMods;mouse
ts
mouse: MouseState;net
ts
net: NetConfig;The net block of game-config.options, read in init.
nextBody
ts
nextBody: number;Next guest-minted body id. Counters start at 1; 0 means "none".
nextSound
ts
nextSound: number;Next guest-minted sound handle.
packer
ts
packer: TransformPacker;player
ts
player: number;The player entity, when the declarative player block created one.
players
ts
players: PlayerTable;frame-input.players, decoded into slots made in init.
rng
ts
rng: Rng;world
ts
world: object;The bitecs world. Typed loosely so the SDK does not leak bitecs generics.
SaveGameDataCmd
Persist the room's game-wide document (JSON). Authority only.
Properties
data
ts
data: string;SavePlayerDataCmd
Persist one player's document (JSON). Authority only.
Properties
data
ts
data: string;player
ts
player: number;SayCmd
Speak a line, optionally with audio and a viseme track.
Properties
audio?
ts
optional audio?: number;entity
ts
entity: number;text
ts
text: string;visemes?
ts
optional visemes?: string;SendCmd
Send a game message to one player, every player, or up to the authority.
Properties
name
ts
name: string;payload
ts
payload: string;JSON, at most 2,048 bytes.
reliable
ts
reliable: boolean;to?
ts
optional to?: number;One player; absent sends to every player (authority) or to the authority (client).
SendOptions
Options for NetFacade.send.
Example
ts
ctx.net.send('role', { role: 'murderer' }, { to: 3 });
ctx.net.send('aim', { x: 1 }, { reliable: false }); // every player, may dropProperties
reliable?
ts
optional reliable?: boolean;Ordered and guaranteed. Default true.
to?
ts
optional to?: number;One player; absent sends to every player. Ignored on a client.
SetAnimCmd
Play or cross-fade an animation clip on one layer.
Properties
clip
ts
clip: string;entity
ts
entity: number;fadeMs
ts
fadeMs: number;looping
ts
looping: boolean;speed
ts
speed: number;weight
ts
weight: number;SetAssetCmd
Attach or detach an entity's renderable.
Properties
asset?
ts
optional asset?: number;entity
ts
entity: number;SetBodyEnabledCmd
Enable or disable a body in the broad phase.
Properties
body
ts
body: number;enabled
ts
enabled: boolean;SetBodyTransformCmd
Move a body directly.
Properties
body
ts
body: number;position
ts
position: Vec3;rotation
ts
rotation: Quat;teleport
ts
teleport: boolean;SetBodyVelocityCmd
Overwrite a body's velocities. undefined leaves one untouched.
Properties
angular?
ts
optional angular?: Vec3;body
ts
body: number;linear?
ts
optional linear?: Vec3;SetCharacterStateCmd
Drive a character's locomotion state machine.
Properties
entity
ts
entity: number;grounded
ts
grounded: boolean;state
ts
state: string;velocity
ts
velocity: Vec3;SetClipWeightsCmd
Set explicit per-clip weights on a character's body layer.
Properties
clips
ts
clips: readonly string[];entity
ts
entity: number;timeScale
ts
timeScale: number;weights
ts
weights: readonly number[] | Float32Array<ArrayBufferLike>;SetExpressionCmd
Set a character's facial expression coefficients.
Properties
entity
ts
entity: number;space
ts
space: ExpressionSpace;weights
ts
weights: readonly number[] | Float32Array<ArrayBufferLike>;SetListenerCmd
Place the audio listener.
Properties
position
ts
position: Vec3;rotation
ts
rotation: Quat;velocity
ts
velocity: Vec3;SetMaterialParamCmd
Set one material uniform.
Properties
entity
ts
entity: number;name
ts
name: string;value
ts
value: MaterialValue;SetParentCmd
Reparent an entity in the scene graph.
Properties
entity
ts
entity: number;keepWorldTransform
ts
keepWorldTransform: boolean;parent?
ts
optional parent?: number;SetPlayerCameraCmd
The camera one player renders from.
Properties
camera
ts
camera: CameraState;player
ts
player: number;SetPlayerEntityCmd
Which entity a player controls. The authority sends it when it spawns definition.player for a join and on every PlayerHandle.possess; the host keeps the mapping (relevancy, "which entity is mine" on the client).
Properties
entity
ts
entity: number;player
ts
player: number;SetPlayerHudCmd
One player's HUD model as JSON.
Properties
hud
ts
hud: string;player
ts
player: number;Shape
Collision shape description carried by add-body.
Properties
asset?
ts
optional asset?: number;halfExtents
ts
halfExtents: Vec3;kind
ts
kind: ShapeKind;SidedSystem
A system that says where it runs.
'authority' runs on the room's authority and in a single-player (solo) game; 'client' runs on a player's page and in solo; 'both' runs everywhere. solo is its own authority and its own client, so it runs all three. A bare function is 'authority' once the game declares features.multiplayer, and 'both' otherwise, so a single-player game reads the same as it always did.
Example
ts
import { defineGame } from 'gameable';
export default defineGame({
features: { multiplayer: true },
systems: [
(ctx) => { ctx.net.send('tick', ctx.frame); }, // bare: the authority's
{ on: 'client', run: (ctx) => { ctx.hud.set({ ping: ctx.frame }); } },
],
});Properties
on
ts
on: "authority" | "client" | "both";Where it runs.
run
ts
run: Run;The system.
SoundEndedEvent
A playing sound stopped.
Properties
completed
ts
completed: boolean;sound
ts
sound: number;SpawnCharacterCmd
Instantiate a splat character bundle.
Properties
bundle
ts
bundle: number;entity
ts
entity: number;position
ts
position: Vec3;rotation
ts
rotation: Quat;SpawnCmd
Create an entity, optionally with a renderable asset.
Properties
asset?
ts
optional asset?: number;entity
ts
entity: number;name?
ts
optional name?: string;parent?
ts
optional parent?: number;position
ts
position: Vec3;rotation
ts
rotation: Quat;scale
ts
scale: Vec3;visible
ts
visible: boolean;SpawnSpec
One entry of the declarative spawns list.
Properties
position
ts
position: readonly number[];Where, [x, y, z].
prefab
ts
prefab: PrefabDef;What to spawn.
rotation?
ts
optional rotation?: readonly number[];Rotation, [x, y, z, w]. Defaults to identity.
StopSoundCmd
Stop a playing sound.
Properties
fadeMs
ts
fadeMs: number;sound
ts
sound: number;ThirdPersonClips
Optional authored clips; omit to use the animator's standard locomotion blend.
Properties
fall
ts
fall: string;idle
ts
idle: string;land
ts
land: string;rise
ts
rise: string;run
ts
run: string;walk
ts
walk: string;TransformStore
Position, rotation (xyzw) and scale, one lane per component.
Properties
qw
ts
qw: Float32Array;qx
ts
qx: Float32Array;qy
ts
qy: Float32Array;qz
ts
qz: Float32Array;sx
ts
sx: Float32Array;sy
ts
sy: Float32Array;sz
ts
sz: Float32Array;x
ts
x: Float32Array;y
ts
y: Float32Array;z
ts
z: Float32Array;Vec3
A 3-component vector.
Properties
x
ts
x: number;y
ts
y: number;z
ts
z: number;VelocityStore
Linear and angular velocity in metres and radians per second.
Properties
ax
ts
ax: Float32Array;ay
ts
ay: Float32Array;az
ts
az: Float32Array;x
ts
x: Float32Array;y
ts
y: Float32Array;z
ts
z: Float32Array;WorldSpec
World-level settings.
Properties
gravity?
ts
optional gravity?: number | readonly number[];Gravity in metres per second squared, applied by the built-in velocity system to entities that have Velocity but no RigidBody. Bodies get their gravity from the host physics world instead.
maxEntities?
ts
optional maxEntities?: number;Entity ceiling. Sizes every built-in component array. Default 4096.
Type Aliases
AssetId
ts
type AssetId = number;Manifest asset handle, resolved from a string id.
AssetKind
ts
type AssetKind = "splat" | "gltf" | "character" | "audio" | "collider" | "data";Manifest asset family.
AudioBus
ts
type AudioBus = "master" | "music" | "sfx" | "voice" | "ui";Audio mixer bus.
BodyId
ts
type BodyId = number;Rigid body / character controller handle.
BodyKind
ts
type BodyKind = "fixed" | "kinematic" | "dynamic" | "character";Physics body class. fixed is what other engines call static.
CameraMode
ts
type CameraMode = "first-person" | "third-person" | "free" | "fixed";Camera rig family.
Command
ts
type Command =
| {
tag: "conversation";
val: ConversationCmd;
}
| {
tag: "spawn";
val: SpawnCmd;
}
| {
tag: "despawn";
val: Entity;
}
| {
tag: "set-asset";
val: SetAssetCmd;
}
| {
tag: "set-parent";
val: SetParentCmd;
}
| {
tag: "set-anim";
val: SetAnimCmd;
}
| {
tag: "set-material-param";
val: SetMaterialParamCmd;
}
| {
tag: "add-body";
val: AddBodyCmd;
}
| {
tag: "remove-body";
val: BodyId;
}
| {
tag: "set-body-transform";
val: SetBodyTransformCmd;
}
| {
tag: "set-body-velocity";
val: SetBodyVelocityCmd;
}
| {
tag: "apply-impulse";
val: ApplyImpulseCmd;
}
| {
tag: "set-body-enabled";
val: SetBodyEnabledCmd;
}
| {
tag: "move-character";
val: MoveCharacterCmd;
}
| {
tag: "spawn-character";
val: SpawnCharacterCmd;
}
| {
tag: "set-character-state";
val: SetCharacterStateCmd;
}
| {
tag: "set-clip-weights";
val: SetClipWeightsCmd;
}
| {
tag: "set-expression";
val: SetExpressionCmd;
}
| {
tag: "look-at";
val: LookAtCmd;
}
| {
tag: "say";
val: SayCmd;
}
| {
tag: "play-sound";
val: PlaySoundCmd;
}
| {
tag: "stop-sound";
val: StopSoundCmd;
}
| {
tag: "set-listener";
val: SetListenerCmd;
}
| {
tag: "load-asset";
val: LoadAssetCmd;
}
| {
tag: "set-pointer-lock";
val: boolean;
}
| {
tag: "set-time-scale";
val: number;
}
| NetCommand;Everything the guest can ask the host to do, applied front to back.
CommandTag
ts
type CommandTag = Command["tag"];Every Command['tag'], useful for exhaustive host-side switches.
ContactPhase
ts
type ContactPhase = "begin" | "stay" | "end";Lifecycle phase of a contact.
Entity
ts
type Entity = number;ECS entity handle. 0 is reserved and means "none".
ErrorCode
ts
type ErrorCode =
| "init-failed"
| "tick-failed"
| "invalid-state"
| "unsupported"
| "asset-missing"
| "snapshot-version-mismatch"
| "internal";Machine-readable half of a GameError.
ExpressionSpace
ts
type ExpressionSpace = "arkit52" | "gnm" | "gnm68";Coordinate space for setExpression weights.
FeatureOptions
ts
type FeatureOptions = Readonly<Record<string, Readonly<Record<string, unknown>>>>;Declared features, normalised: every present feature has an options object.
GameDefinition
ts
type GameDefinition = Readonly<GameSpec>;A validated game declaration.
GameEvent
ts
type GameEvent =
| {
tag: "conversation-event";
val: ConversationEvent;
}
| {
tag: "asset-loaded";
val: AssetLoadedEvent;
}
| {
tag: "character-ready";
val: CharacterReadyEvent;
}
| {
tag: "sound-ended";
val: SoundEndedEvent;
}
| {
tag: "anim-event";
val: AnimEventData;
}
| {
tag: "resized";
val: ResizedEvent;
}
| NetEvent;A host-side occurrence since the previous tick.
KeyName
ts
type KeyName = string;Any string this table accepts: a DOM code, a bare letter or digit, or one of the friendly aliases.
LogLevel
ts
type LogLevel = "trace" | "debug" | "info" | "warn" | "error";Log severity accepted by env.log.
LogSink
ts
type LogSink = (level, msg) => void;Where prelude console output goes.
Parameters
| Parameter | Type |
|---|---|
level | LogLevel |
msg | string |
Returns
void
MaterialValue
ts
type MaterialValue =
| {
tag: "scalar";
val: number;
}
| {
tag: "boolean";
val: boolean;
}
| {
tag: "color";
val: Rgba;
}
| {
tag: "vector";
val: Vec3;
}
| {
tag: "texture";
val: AssetId;
};A material parameter value.
MessageCheck
ts
type MessageCheck<T> = (payload) => payload is T;The game's own type guard for a payload, already parsed from JSON. It must be pure: no wall time, no Math.random, no state, so the authority and every replay agree on what it drops.
Type Parameters
| Type Parameter |
|---|
T |
Parameters
| Parameter | Type |
|---|---|
payload | unknown |
Returns
payload is T
NetCommand
ts
type NetCommand =
| {
tag: "send";
val: SendCmd;
}
| {
tag: "set-player-camera";
val: SetPlayerCameraCmd;
}
| {
tag: "set-player-hud";
val: SetPlayerHudCmd;
}
| {
tag: "save-player-data";
val: SavePlayerDataCmd;
}
| {
tag: "save-game-data";
val: SaveGameDataCmd;
}
| {
tag: "set-player-entity";
val: SetPlayerEntityCmd;
}
| {
tag: "exchange";
val: ExchangeCmd;
};The net and data members of the command variant.
NetEvent
ts
type NetEvent =
| {
tag: "player-joined";
val: PlayerJoinedEvent;
}
| {
tag: "player-left";
val: PlayerLeftEvent;
}
| {
tag: "message";
val: NetMessageEvent;
}
| {
tag: "exchange-result";
val: ExchangeResultEvent;
}
| {
tag: "game-data";
val: GameDataEvent;
};The net and data members of the event variant.
NetRole
ts
type NetRole = "solo" | "authority" | "client";Where a guest runs: alone (solo), as the room's authority, or as one player's client.
Example
ts
const role: NetRole = ctx.net.role;
if (role === 'client') showLatency();PlayerSpawn
ts
type PlayerSpawn =
| readonly number[]
| readonly readonly number[][]
| ((seat, ctx) => Vec3);Where definition.player spawns each seat: one point [x, y, z] for everyone, a list of points (seat i takes list[i % list.length]), or a function of the seat. A single-player game spawns seat 0.
The function runs on the authority at the seat's join, inside the step, so it must be deterministic: read only the seat, ctx.rules, ctx.rng and the world, never the clock or Math.random. It runs before ctx.players is updated for the step, so read the seat, not ctx.players: the map still holds the previous step's players there.
Example
ts
import { defineGame, type PlayerSpawn } from 'gameable';
const corners: PlayerSpawn = [[-8, 0, -8], [8, 0, -8], [8, 0, 8], [-8, 0, 8]];
const ring: PlayerSpawn = (seat) => ({ x: Math.cos(seat) * 6, y: 0, z: Math.sin(seat) * 6 });
export default defineGame({ player: { prefab: Hero, spawn: corners } });ProjectionKind
ts
type ProjectionKind = "perspective" | "orthographic";Camera projection family.
ShapeKind
ts
type ShapeKind =
| "box"
| "sphere"
| "capsule"
| "cylinder"
| "plane"
| "convex-hull"
| "mesh"
| "height-field";Collision shape family.
SoundId
ts
type SoundId = number;Playing-sound handle.
System
ts
type System = (ctx) => void;A system: one plain function, run once per fixed step. In systems it may also be a SidedSystem, { run, on }, that says where it runs.
Parameters
| Parameter | Type |
|---|---|
ctx | GameContext |
Returns
void
TransferDoc
ts
type TransferDoc = Record<string, unknown>;A player document as a transfer reads it: a plain object.
Variables
audio
ts
const audio: object;The audio facade.
Type Declaration
listener()
ts
listener(position, rotation): void;Place the audio listener.
Parameters
| Parameter | Type | Description |
|---|---|---|
position | Vec3 | Listener position. |
rotation | Quat | Listener rotation, xyzw. |
Returns
void
Nothing.
play()
ts
play(asset, options?): number;Start a sound.
Parameters
| Parameter | Type | Description |
|---|---|---|
asset | string | number | Manifest string id or asset handle. |
options? | PlayOptions | Attachment, gain, pitch, looping and bus. |
Returns
number
The guest-minted sound handle, or 0 when the asset is unknown.
stop()
ts
stop(sound, fadeMs?): void;Stop a playing sound.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
sound | number | undefined | A handle from play. |
fadeMs | number | 0 | Fade-out in milliseconds; 0 stops immediately. |
Returns
void
Nothing.
Example
ts
import { audio } from 'gameable';
const id = audio.play('shot', { entity: player, volume: 0.8 });
audio.stop(id, 50);AUTHORITY_SENDER
ts
const AUTHORITY_SENDER: 4294967295 = 0xffffffff;The sender id of a message from the authority itself. Room seats start at 0, so the authority is not 0: this is past every player id and still a u32 (the WIT's message-event.player). Keep ids unsigned: in an Int32Array this reads back as -1.
Example
ts
import { AUTHORITY_SENDER } from 'gameable';
const fromServer = (from: number): boolean => from === AUTHORITY_SENDER;BODY_KINDS
ts
const BODY_KINDS: readonly ["fixed", "kinematic", "dynamic", "character"];Physics body classes, in the index order RigidBody.kind stores.
BODY_STRIDE
ts
const BODY_STRIDE: 15 = 15;Floats per row of frame-input.bodies.
BUILTIN_COMPONENT_NAMES
ts
const BUILTIN_COMPONENT_NAMES: readonly ["Transform", "Renderable", "RigidBody", "Character", "Velocity", "Health", "Player", "Enemy", "Pickup"];Names matching BUILTIN_COMPONENTS, used in snapshot headers.
BUILTIN_COMPONENTS
ts
const BUILTIN_COMPONENTS: readonly [TransformStore, RenderableStore, RigidBodyStore, CharacterStore, VelocityStore, HealthStore, Record<string, never>, Record<string, never>, Record<string, never>];Every built-in component, in the fixed order snapshot serialises them.
camera
ts
const camera: object;The camera facade: writes the frame's camera.
Type Declaration
look
Get Signature
ts
get look(): object;Accumulated look angles, in radians. Mutate to snap the view.
Returns
object
The live look state.
pitch
ts
pitch: number;sensitivity
ts
sensitivity: number;yaw
ts
yaw: number;state
Get Signature
ts
get state(): CameraState;The whole camera record, for games that want every knob.
Returns
The live record. Mutate it; do not replace it.
firstPerson()
ts
firstPerson(entity, options?): void;Mount the camera at an entity's eyes.
Yaw and pitch come from the built-in look accumulator, which integrates input.mouse.dx/dy once per tick.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity to mount on. |
options? | FirstPersonOptions | Eye height and field of view. |
Returns
void
Nothing.
follow()
ts
follow(entity, options?): void;Put the camera on a spring arm behind an entity.
The host owns the arm and its collision; the guest only states the intent. position is the orbit pivot and rotation the direction the player is looking, so a host with no rig still ends up somewhere sensible.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity to follow. |
options? | FollowOptions | Boom length, height offset, field of view and orbit angles. |
Returns
void
Nothing.
lookAt()
ts
lookAt(point): void;Aim the camera at a world point, overriding its rotation.
Parameters
| Parameter | Type | Description |
|---|---|---|
point | Vec3 | null | The point to look at, or null to use the rotation again. |
Returns
void
Nothing.
set()
ts
set(
position,
rotation,
fovYDeg?
): void;Place the camera explicitly, detaching it from any entity.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
position | Vec3 | undefined | Eye position. |
rotation | Quat | undefined | Eye rotation, xyzw. |
fovYDeg | number | 60 | Vertical field of view in degrees. Default 60. |
Returns
void
Nothing.
Example
ts
import { camera } from 'gameable';
camera.firstPerson(player, { eyeHeight: 1.7 });character
ts
const character: object;The character facade.
Type Declaration
bundleOf()
ts
bundleOf(entity): number;The character bundle handle attached to an entity, or 0.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity id. |
Returns
number
The bundle asset handle.
lookAt()
ts
lookAt(
entity,
target,
weight?
): void;Aim the head and eyes at a world point.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
entity | number | undefined | Entity with a Character component. |
target | Vec3 | null | undefined | The point, or null to release and return to the idle. |
weight | number | 1 | Blend weight in 0..1. Default 1. |
Returns
void
Nothing.
say()
ts
say(
entity,
text,
voice?,
visemes?
): void;Speak a line.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity with a Character component. |
text | string | Subtitle text; the host decides whether to show it. |
voice? | string | number | Manifest string id or handle of a voice line. |
visemes? | string | Viseme track as JSON, matching the bundle's space. |
Returns
void
Nothing.
setClipWeights()
ts
setClipWeights(
entity,
clips,
weights,
timeScale?
): void;Set explicit per-clip weights on the body layer.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
entity | number | undefined | Entity with a Character component. |
clips | readonly string[] | undefined | Clip names. |
weights | readonly number[] | Float32Array<ArrayBufferLike> | undefined | Positional weights; must be the same length as clips. |
timeScale | number | 1 | Playback rate for the whole layer. Default 1. |
Returns
void
Nothing.
setExpression()
ts
setExpression(
entity,
space,
weights
): void;Set facial expression coefficients.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity with a Character component. |
space | ExpressionSpace | Coordinate space: 52 ARKit, 387 GNM, or the 68-float view. |
weights | readonly number[] | Float32Array<ArrayBufferLike> | Coefficients; length must match the space. |
Returns
void
Nothing.
setState()
ts
setState(
entity,
state,
vx,
vy,
vz,
grounded?
): void;Drive the locomotion state machine.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
entity | number | undefined | Entity with a Character component. |
state | string | undefined | State name, for example `'idle' |
vx | number | undefined | World-space velocity x, drives the locomotion blend. |
vy | number | undefined | World-space velocity y. |
vz | number | undefined | World-space velocity z. |
grounded | boolean | true | Whether the character is on the ground. |
Returns
void
Nothing.
Example
ts
import { character } from 'gameable';
character.setState(npc, 'walk', 0, 0, 1.4, true);
character.lookAt(npc, { x: 0, y: 1.6, z: 0 });Character
ts
const Character: CharacterStore;Splat character bundle attached to an entity.
conversation
ts
const conversation: object;Structural interview controls; all streams remain in optional host modules.
Type Declaration
command()
ts
command(
entity,
action,
character?,
text?
): void;Queue one control using the reusable command pool.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
entity | number | undefined | Interview entity. |
action | | "start" | "end" | "ask" | "interrupt" | "microphone-on" | "microphone-off" | undefined | Lifecycle or question action. |
character | string | '' | Host-configured id. |
text | string | '' | Question, if any. |
Returns
void
Example
ts
import { conversation } from 'gameable';
conversation.command(npc, 'start', 'steward');
conversation.command(npc, 'ask', 'steward', 'Who had the study key?');DEFAULT_MAX_ENTITIES
ts
const DEFAULT_MAX_ENTITIES: 4096 = 4096;Default entity ceiling. Every built-in component array is this long.
DEFAULT_MAX_PLAYERS
ts
const DEFAULT_MAX_PLAYERS: 16 = 16;The highest player id a guest makes a slot for when net.maxPlayers is not in game-config.options.
Example
ts
import { DEFAULT_MAX_PLAYERS } from 'gameable';
const options = JSON.stringify({ net: { maxPlayers: DEFAULT_MAX_PLAYERS * 2 } });DEFAULT_ROOM_SEATS
ts
const DEFAULT_ROOM_SEATS: 8 = 8;Seats in a room when the game declares features.multiplayer without a maxPlayers.
Example
ts
import { DEFAULT_ROOM_SEATS, defineGame, roomSeats } from 'gameable';
const game = defineGame({ features: { multiplayer: true } });
roomSeats(game) === DEFAULT_ROOM_SEATS; // trueEnemy
ts
const Enemy: Record<string, never> = {};Tag: a hostile entity.
EXPRESSION_DIMS
ts
const EXPRESSION_DIMS: Readonly<Record<ExpressionSpace, number>>;Coefficient counts each expression space expects.
Health
ts
const Health: HealthStore;Hit points.
hud
ts
const hud: object;The HUD facade: writes the frame's HUD.
Type Declaration
clear()
ts
clear(): void;Drop the HUD: emit an empty model this frame.
Idempotent — calling it every frame emits {} once, exactly like set.
Returns
void
Nothing.
invalidate()
ts
invalidate(): void;Forget what the host has seen, so the next set emits even if the model did not change. Use it after the host reloaded its overlay.
On its own it sends nothing: the next set does. To send an empty model now, call clear().
Returns
void
Nothing.
set()
ts
set(model): boolean;Set the HUD model for this frame.
Nested plain objects are compared by value four levels down, so a model rebuilt inline every frame is recognised as unchanged. Arrays and class instances are compared by identity: keep those out of the model, or build them once and mutate nothing.
Parameters
| Parameter | Type | Description |
|---|---|---|
model | Record<string, unknown> | A JSON-serialisable object. |
Returns
boolean
True when the model changed and JSON will cross this frame.
Example
ts
import { hud } from 'gameable';
hud.set({ health: 100, ammo: 30 }); // emitted once
hud.set({ health: 100, ammo: 30 }); // unchanged, nothing crossesinput
ts
const input: object;The keyboard, mouse and gamepad facade: the local player's input. That is frame-input.input in a single-player game and on the authority, and the local player's slot of frame-input.players on a client.
Type Declaration
focused
Get Signature
ts
get focused(): boolean;Does the canvas have focus? Treat input as neutral when it does not.
Returns
boolean
True while the canvas is focused.
mods
Get Signature
ts
get mods(): InputMods;Keyboard modifier state.
Returns
The modifiers for this frame.
mouse
Get Signature
ts
get mouse(): MouseState;Pointer position, per-frame delta, wheel and button bitsets.
Returns
The mouse state for this frame. Owned by the SDK; never retain it.
axis2()
ts
axis2(
negX,
posX,
negY,
posY
): Axis2;A two-axis reading built from four keys.
Parameters
| Parameter | Type | Description |
|---|---|---|
negX | string | Key that drives x negative, for example 'A'. |
posX | string | Key that drives x positive, for example 'D'. |
negY | string | Key that drives y negative, for example 'S'. |
posY | string | Key that drives y positive, for example 'W'. |
Returns
A pooled { x, y } with components in -1..1. Never retain it.
gamepad()
ts
gamepad(index):
| {
axes: ArrayLike<number>;
buttons: number;
connected: boolean;
index: number;
pressed: number;
released: number;
}
| null;One connected gamepad.
Parameters
| Parameter | Type | Description |
|---|---|---|
index | number | Navigator gamepad index. |
Returns
| { axes: ArrayLike<number>; buttons: number; connected: boolean; index: number; pressed: number; released: number; } | null
The gamepad, or null when nothing is connected at that index. Owned by the SDK; never retain it.
isDown()
ts
isDown(key): boolean;Is the key held this frame?
Parameters
| Parameter | Type | Description |
|---|---|---|
key | string | A DOM code ('KeyW'), a bare letter/digit ('W', '1') or an alias ('Shift', 'Esc'). |
Returns
boolean
True while the key is down.
mouseDown()
ts
mouseDown(button): boolean;Is a mouse button held?
Parameters
| Parameter | Type | Description |
|---|---|---|
button | number | A MOUSE_BUTTONS value, or a raw bit mask. |
Returns
boolean
True while the button is down.
mousePressed()
ts
mousePressed(button): boolean;Did a mouse button go down this frame?
Parameters
| Parameter | Type | Description |
|---|---|---|
button | number | A MOUSE_BUTTONS value, or a raw bit mask. |
Returns
boolean
True on the frame the button went down.
mouseReleased()
ts
mouseReleased(button): boolean;Did a mouse button come up this frame?
Parameters
| Parameter | Type | Description |
|---|---|---|
button | number | A MOUSE_BUTTONS value, or a raw bit mask. |
Returns
boolean
True on the frame the button came up.
pressed()
ts
pressed(key): boolean;Did the key go down this frame?
Parameters
| Parameter | Type | Description |
|---|---|---|
key | string | A key name. |
Returns
boolean
True on the frame the key went down.
released()
ts
released(key): boolean;Did the key come up this frame?
Parameters
| Parameter | Type | Description |
|---|---|---|
key | string | A key name. |
Returns
boolean
True on the frame the key came up.
Example
ts
import { input } from 'gameable';
const move = input.axis2('A', 'D', 'S', 'W'); // x = strafe, y = forward
if (input.pressed('Space')) jump();
if (input.mouseDown(1)) fire();IS_COMPONENT_GUEST
ts
const IS_COMPONENT_GUEST: boolean;True when this realm looks like the QuickJS component guest rather than V8.
Decided once, before anything is polyfilled: a realm with neither console nor TextEncoder is componentize-qjs. It is the only realm whose Math.random the prelude is allowed to replace — patching the global in V8 would reach vitest, Vite and the host application too.
KEY_BITS
ts
const KEY_BITS: 256 = 256;Number of key bits the WIT key-state bitsets can carry.
KEY_COUNT
ts
const KEY_COUNT: number = KEY_NAMES.length;Number of key names currently assigned.
KEY_NAMES
ts
const KEY_NAMES: readonly string[];Every key name in index order. The array index is the bit index.
Do not reorder; append only.
KEY_WORDS
ts
const KEY_WORDS: number;Number of u32 words in one key-state bitset.
LAYER_KEYS
ts
const LAYER_KEYS: readonly ["defaultLayer", "staticGeometry", "player", "enemy", "projectile", "pickup", "trigger", "character", "debris", "water", "user0", "user1", "user2", "user3", "user4", "user5"];Every WIT collision-layers member, in declaration order.
Bit i of the physics module's numeric layer/mask is member i of this list. It is a contract between the guest, which writes the flags record, and the host, which folds it into a bitmask — so both sides import this one list rather than keeping a copy each.
Example
ts
import { LAYER_KEYS } from 'gameable';
const bit = LAYER_KEYS.indexOf('enemy'); // 3MOUSE_BUTTONS
ts
const MOUSE_BUTTONS: Readonly<{
BACK: 8;
FORWARD: 16;
LEFT: 1;
MIDDLE: 4;
RIGHT: 2;
}>;Mouse button bit positions, matching mouse-state.buttons.
PACKAGE
ts
const PACKAGE: "@gameable/sdk";Package identity marker.
Example
ts
import { PACKAGE } from 'gameable';
console.log(PACKAGE); // 'gameable'physics
ts
const physics: object;Physics queries and body commands.
Type Declaration
ALL_LAYERS
ts
ALL_LAYERS: CollisionLayers;Every collision layer, for queries that should hit anything.
applyImpulse()
ts
applyImpulse(
entity,
x,
y,
z,
atX?,
atY?,
atZ?
): void;Apply a one-shot impulse.
With no application point the impulse acts at the centre of mass. Give one — all three coordinates — to apply it off-centre and impart spin.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity with a body. |
x | number | Impulse x, newton-seconds. |
y | number | Impulse y. |
z | number | Impulse z. |
atX? | number | World-space application point x, or omit for the centre of mass. |
atY? | number | Application point y. |
atZ? | number | Application point z. |
Returns
void
Nothing.
isGrounded()
ts
isGrounded(entity): boolean;Actual walkable ground contact from the last host physics step. No host call. Unknown, steep and unsupported contacts return false, even at a jump apex.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Character entity. |
Returns
boolean
Whether the character is supported by walkable ground.
moveCharacter()
ts
moveCharacter(
entity,
vx,
vy,
vz,
jump?,
crouch?,
maxSlopeDeg?
): void;Drive a character body for this step.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
entity | number | undefined | Entity whose body was created with kind: 'character'. |
vx | number | undefined | Desired world-space velocity x, metres per second. |
vy | number | undefined | Desired world-space velocity y. |
vz | number | undefined | Desired world-space velocity z. |
jump | boolean | false | Request a jump this step. |
crouch | boolean | false | Request a crouch this step. |
maxSlopeDeg | number | 45 | Maximum walkable slope. |
Returns
void
Nothing.
overlapSphere()
ts
overlapSphere(
center,
radius,
maxResults?,
mask?,
ignoreEntity?
): readonly OverlapHit[];Bodies overlapping a sphere, nearest first.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
center | Vec3 | undefined | Sphere centre. |
radius | number | undefined | Sphere radius in metres. |
maxResults | number | 16 | Cap on returned hits. |
mask? | CollisionLayers | undefined | Layers to consider; defaults to every layer. |
ignoreEntity? | number | undefined | Entity to skip. |
Returns
readonly OverlapHit[]
The overlapping bodies.
raycast()
ts
raycast(
origin,
direction,
maxDistance,
mask?,
ignoreEntity?
): RayHit | null;Closest hit along a ray.
Parameters
| Parameter | Type | Description |
|---|---|---|
origin | Vec3 | World-space ray origin. |
direction | Vec3 | Ray direction; need not be normalised. |
maxDistance | number | Maximum distance in metres. |
mask? | CollisionLayers | Layers to consider; defaults to every layer. |
ignoreEntity? | number | Entity to skip, usually the caster. |
Returns
RayHit | null
The hit, or null on a miss. The hit object comes from the host and is freshly allocated: this call is not allocation-free.
raycastBatch()
ts
raycastBatch(rays): readonly (RayHit | null | undefined)[];Many rays in one round trip. Result index i matches rays[i].
Parameters
| Parameter | Type | Description |
|---|---|---|
rays | readonly object[] | The rays. Build them once and mutate them in place. |
Returns
readonly (RayHit | null | undefined)[]
One result per ray; undefined or null entries are misses.
setEnabled()
ts
setEnabled(entity, enabled): void;Enable or disable a body in the broad phase.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity with a body. |
enabled | boolean | Whether the body participates. |
Returns
void
Nothing.
setVelocity()
ts
setVelocity(
entity,
x,
y,
z,
ax?,
ay?,
az?
): void;Overwrite a body's velocity.
Angular velocity is left alone unless all three angular components are given.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity with a body. |
x | number | Linear velocity x. |
y | number | Linear velocity y. |
z | number | Linear velocity z. |
ax? | number | Angular velocity x, radians per second. |
ay? | number | Angular velocity y. |
az? | number | Angular velocity z. |
Returns
void
Nothing.
teleport()
ts
teleport(
entity,
x,
y,
z
): void;Teleport a body, clearing its velocities.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | Entity with a body. |
x | number | Position x. |
y | number | Position y. |
z | number | Position z. |
Returns
void
Nothing.
Example
ts
import { physics, Transform } from 'gameable';
const hit = physics.raycast(eye, forward, 100);
if (hit) console.log('hit entity', hit.entity, 'at', hit.distance);Pickup
ts
const Pickup: Record<string, never> = {};Tag: something the player can pick up.
Player
ts
const Player: Record<string, never> = {};Tag: the entity the player controls.
Renderable
ts
const Renderable: RenderableStore;Renderable asset attached to an entity.
RigidBody
ts
const RigidBody: RigidBodyStore;Physics body attached to an entity.
SHAPE_KINDS
ts
const SHAPE_KINDS: readonly ["box", "sphere", "capsule", "cylinder", "plane", "convex-hull", "mesh", "height-field"];Collision shapes, in the index order RigidBody.shape stores.
SNAPSHOT_VERSION
ts
const SNAPSHOT_VERSION: 3 = 3;Bumped whenever the byte layout changes. Mismatches refuse to restore.
TAG_ORDINAL
ts
const TAG_ORDINAL: Record<CommandTag, number>;Every command tag mapped to its pool index.
Written out rather than derived so the compiler checks it: Record over the CommandTag union rejects both a missing tag and one that is not in the WIT variant. packages/wasm-host/src/apply.test.ts pins the same list from the host side.
Example
ts
import { TAG_ORDINAL } from 'gameable';
console.log(TAG_ORDINAL['set-time-scale']); // 24Transform
ts
const Transform: TransformStore;World transform of an entity. Position, rotation (xyzw), scale.
TRANSFORM_ALL
ts
const TRANSFORM_ALL: number;POSITION | ROTATION | SCALE, what a freshly spawned entity needs.
TRANSFORM_FLAGS
ts
const TRANSFORM_FLAGS: Readonly<{
DESTROYED: 32;
POSITION: 1;
ROTATION: 2;
SCALE: 4;
TELEPORT: 16;
VISIBLE: 8;
}>;Meaning of lane 1 of a transform row, matching WIT transform-flags.
TRANSFORM_STRIDE
ts
const TRANSFORM_STRIDE: 12 = 12;Floats per row of frame-output.transforms.
Velocity
ts
const Velocity: VelocityStore;Guest-integrated velocity, for entities the host physics does not own.
Functions
activeRuntime()
ts
function activeRuntime(): RuntimeState;The runtime the SDK facades are currently bound to.
Returns
The active runtime.
Throws
When called outside init, a system, update or shutdown.
applyTransfer()
ts
function applyTransfer(
docA,
docB,
give,
take
):
| {
a: TransferDoc;
b: TransferDoc;
}
| null;Trade between two player documents: a gives b everything in give, and takes from b everything in take. A number field moves that amount (the giver must have at least that much); a list field moves those items (the giver must hold each one; an object item matches by value, whatever its key order). A missing document counts as {}.
Parameters
| Parameter | Type | Description |
|---|---|---|
docA | unknown | Player a's document, or null. |
docB | unknown | Player b's document, or null. |
give | unknown | What a gives b, such as { coins: 5 }. |
take | unknown | What a takes from b, such as { owned: ['gem'] }. |
Returns
| { a: TransferDoc; b: TransferDoc; } | null
Both new documents (the inputs are untouched), or null when the trade is impossible.
Example
ts
import { applyTransfer } from 'gameable';
applyTransfer({ coins: 10 }, { owned: ['gem'] }, { coins: 4 }, { owned: ['gem'] });
// { a: { coins: 6, owned: ['gem'] }, b: { owned: [], coins: 4 } }assetId()
ts
function assetId(name): number;Resolve a manifest string id to an asset handle, caching the result.
Parameters
| Parameter | Type | Description |
|---|---|---|
name | string | The manifest string id, for example 'arena'. |
Returns
number
The handle, or 0 when the manifest has no such entry.
Example
ts
import { assetId } from 'gameable';
const arena = assetId('arena');commandPoolSize()
ts
function commandPoolSize(): number;Total pooled command slots created since the module loaded.
Tests assert this stops growing once a game reaches its steady state.
Returns
number
The number of slots ever allocated.
Example
ts
const before = commandPoolSize();
for (let i = 0; i < 100; i += 1) guest.tick(input);
console.log(commandPoolSize() === before); // true, in a steady stateconfigureEcs()
ts
function configureEcs(n): void;Resize every built-in component array.
Call this before defineGame runs any system — the runtime calls it from init out of world.maxEntities. The component objects keep their identity, so bitecs registrations survive; only the arrays inside are replaced, so never cache Transform.x across a call.
Parameters
| Parameter | Type | Description |
|---|---|---|
n | number | The new entity ceiling. Values below 2 are clamped to 2. |
Returns
void
Nothing.
createGuest()
ts
function createGuest(host, definition): Guest;Create a guest from a host and a game definition.
Parameters
| Parameter | Type | Description |
|---|---|---|
host | HostApi | The host services, in guest-side JS shapes. |
definition | GameDefinition | The result of defineGame. |
Returns
The five WIT exports, plus dead and state.
Example
ts
import { createGuest } from 'gameable';
import { createMockHost, createFrameInput } from 'gameable/test';
const guest = createGuest(createMockHost(), game);
guest.init({ seed: 1, fixedHz: 60, viewportWidth: 1, viewportHeight: 1, devMode: true });
const out = guest.tick(createFrameInput({ frame: 0 }));createRng()
ts
function createRng(initialSeed?): Rng;Create a seeded xoshiro128** generator.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
initialSeed | number | 1 | A 32-bit seed. 0 is remapped so the state is never all zero. |
Returns
A generator whose whole state is four integers.
createThirdPersonController()
ts
function createThirdPersonController(clips?): object;Create one allocation-free camera-relative controller. Call reset in game init; update from a guest system. Tuning comes from ctx.rules: walkSpeed, runSpeed, acceleration, deceleration, jumpSpeed, landingSeconds, moveTurnRate, idleTurnRate, idleTurnThreshold and camera pitch/distance/height.
update drives the single player: ctx.player from ctx.input, with ctx.camera. updatePlayers drives a room on its authority: each player's own entity (PlayerHandle.entity, so a possess moves the controller too) from that player's own input, with that player's own camera. Each seat has its own state, made the first time the seat is seen and reset whenever the seat's entity changes (a join, a respawn, a possess).
Parameters
| Parameter | Type | Description |
|---|---|---|
clips? | ThirdPersonClips | Optional complete base-layer clip mapping, configured once. |
Returns
Persistent state, reset and update functions.
reset
ts
reset: (ctx) => void;Reset the single player's state and look; forget every seat.
Parameters
| Parameter | Type | Description |
|---|---|---|
ctx | GameContext | The init context. |
Returns
void
state
ts
state: object = locomotionState;state.airborne
ts
airborne: number = 0;state.descending
ts
descending: boolean = false;state.facing
ts
facing: number = Math.PI;state.grounded
ts
grounded: boolean = false;state.landing
ts
landing: number = 0;state.pitch
ts
pitch: number = 0;state.speed
ts
speed: number = 0;state.state
ts
state: string = IDLE;state.turning
ts
turning: boolean = false;state.vx
ts
vx: number = 0;state.vz
ts
vz: number = 0;state.yaw
ts
yaw: number = 0;stateOf
ts
stateOf: (player) =>
| {
airborne: number;
descending: boolean;
facing: number;
grounded: boolean;
landing: number;
pitch: number;
speed: number;
state: string;
turning: boolean;
vx: number;
vz: number;
yaw: number;
}
| undefined;Parameters
| Parameter | Type | Description |
|---|---|---|
player | number | A player id. |
Returns
| { airborne: number; descending: boolean; facing: number; grounded: boolean; landing: number; pitch: number; speed: number; state: string; turning: boolean; vx: number; vz: number; yaw: number; } | undefined
That seat's state, or undefined before it was first driven.
update
ts
update: (ctx, freeze) => void;Drive the single player one step.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
ctx | GameContext | undefined | The frame context. |
freeze | boolean | false | True to hold the player still. |
Returns
void
updatePlayers
ts
updatePlayers: (ctx, freeze) => void;Drive every room player's own entity one step, from their own input.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
ctx | GameContext | undefined | The frame context, on the authority. |
freeze | boolean | ((player) => boolean) | false | True to hold everyone still, or a test per player (hoist it: a function made per frame allocates), such as "this player is talking". |
Returns
void
Example
ts
const controller = createThirdPersonController();
// In init: controller.reset(ctx); in a system:
if (ctx.net.role === 'solo') controller.update(ctx);
else controller.updatePlayers(ctx);defineGame()
ts
function defineGame(spec): GameDefinition;Declare a game.
Parameters
| Parameter | Type | Description |
|---|---|---|
spec | GameSpec | The declaration. |
Returns
The frozen definition, ready for createGuest or a build.
Example
ts
import { defineGame, prefab } from 'gameable';
const Player = prefab({
body: { shape: 'capsule', dims: [0.3, 0.9], kind: 'character' },
});
export default defineGame({
assets: ['arena'],
world: { gravity: -9.81 },
player: { prefab: Player, spawn: [0, 1, 0], camera: 'firstPerson' },
systems: [(ctx) => { ctx.hud.set({ frame: ctx.frame }); }],
});defineMessage()
ts
function defineMessage<T>(
name,
check,
options?
): MessageDef<T>;Declare a game message. Call it at module scope, once per name: a second definition with the same name before the next init throws.
A received payload that is over maxBytes, does not parse, fails check or makes check throw is dropped and counted in ctx.net.stats.dropped; a system never sees it.
Type Parameters
| Type Parameter |
|---|
T |
Parameters
| Parameter | Type | Description |
|---|---|---|
name | string | The message name; unique within the game. |
check | MessageCheck<T> | The game's own type guard. Pure: no clock, no randomness. |
options? | MessageOptions | maxBytes, default 2,048 (the wire cap). |
Returns
MessageDef<T>
The definition.
Throws
On a duplicate name, an empty name, a name starting with aos: (reserved for the engine, as ctx.net.setPhase's aos:phase), or a maxBytes that is not a whole number from 1 to 2,048.
Example
ts
import { defineMessage, hasKeys } from 'gameable';
const hasFor = hasKeys('for');
export const Vote = defineMessage(
'vote',
(p): p is { for: number } => hasFor(p) && typeof p.for === 'number',
{ maxBytes: 64 },
);describeAsset()
ts
function describeAsset(idOrName): AssetDesc | null;Metadata for an asset handle or manifest name.
Parameters
| Parameter | Type | Description |
|---|---|---|
idOrName | string | number | A handle from assetId, or a manifest string id. |
Returns
AssetDesc | null
The description, or null when the asset is unknown.
Example
ts
import { describeAsset } from 'gameable';
const desc = describeAsset('myra');
if (desc?.kind === 'character') console.log('rig backend', desc.rig);despawn()
ts
function despawn(entity): void;Destroy an entity, its body and its host-side representation.
Parameters
| Parameter | Type | Description |
|---|---|---|
entity | number | The entity id. |
Returns
void
Nothing.
Example
ts
import { despawn } from 'gameable';
despawn(enemy);featuresOf()
ts
function featuresOf(definition): FeatureOptions;Normalise a game's features block.
Parameters
| Parameter | Type | Description |
|---|---|---|
definition | GameDefinition | The defineGame result. |
Returns
A frozen table: feature name to its options; true becomes {}, false is dropped.
Example
ts
import { defineGame, featuresOf } from 'gameable';
const game = defineGame({ features: { characters: true, multiplayer: { maxPlayers: 6 } } });
console.log(featuresOf(game)); // { characters: {}, multiplayer: { maxPlayers: 6 } }getActiveRuntime()
ts
function getActiveRuntime(): RuntimeState | null;The runtime currently executing, or null outside a guest call.
Returns
RuntimeState | null
The active runtime.
getMaxEntities()
ts
function getMaxEntities(): number;How many entities the built-in component arrays currently hold.
Returns
number
The configured entity ceiling.
hasKeys()
ts
function hasKeys<K>(...keys): (x) => x is Record<K, unknown>;A guard for a record with every one of keys as an own property (any value, even undefined); extra keys are allowed. Make it once, at module scope: the guard it returns allocates nothing per call.
Type Parameters
| Type Parameter |
|---|
K extends string |
Parameters
| Parameter | Type | Description |
|---|---|---|
...keys | K[] | The keys a payload must have. |
Returns
The guard.
(x) => x is Record<K, unknown>
Example
ts
const hasItemSlot = hasKeys('item', 'slot');
const Pick = defineMessage(
'pick',
(p): p is { item: string; slot: number } =>
hasItemSlot(p) && typeof p.item === 'string' && typeof p.slot === 'number',
);isRecord()
ts
function isRecord(x): x is Record<string, unknown>;A plain JSON object: not null, not an array.
Parameters
| Parameter | Type | Description |
|---|---|---|
x | unknown | A parsed payload. |
Returns
x is Record<string, unknown>
True for an object record.
Example
ts
const Move = defineMessage('move', (p): p is { x: number } => isRecord(p) && typeof p.x === 'number');keyIndex()
ts
function keyIndex(name): number;Bit index for a key name, or -1 when the name is unknown. Names are case-sensitive, except that a bare letter or F-key also answers in lower case ('w', 'f1'). Allocates nothing.
Parameters
| Parameter | Type | Description |
|---|---|---|
name | string | A DOM KeyboardEvent.code, a bare letter/digit, or an alias. |
Returns
number
The bit index in 0..255, or -1.
keyIndex2()
ts
function keyIndex2(name): number;Second bit index for names that cover a left/right pair ('Shift'), or -1. Case-sensitive, like the aliases it covers. Allocates nothing.
Parameters
| Parameter | Type | Description |
|---|---|---|
name | string | A key name or alias. |
Returns
number
The second bit index, or -1 when the name maps to one key.
loadAsset()
ts
function loadAsset(idOrName, priority?): void;Ask the host to start loading an asset.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
idOrName | string | number | undefined | A handle or manifest string id. |
priority | number | 0 | Higher runs first. Default 0. |
Returns
void
Nothing.
Example
ts
import { loadAsset } from 'gameable';
loadAsset('boss-arena', 10);markMoved()
ts
function markMoved(entity, flags?): void;Tell the packer an entity's Transform changed.
spawn and the built-in velocity integration mark for you. Two cases they do not cover: a system that writes Transform.x[e] (or any other lane) by hand, and a body-driven entity, whose transform the host now owns outright — BodyIndex.ingest copies the physics rows in for the guest to read and deliberately does not mark them. Either way, nothing notices the write, the row is never packed and the host never moves the object. Call this after such a write. To move a physics body, send a command (setBodyTransform) rather than writing the lane.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
entity | number | undefined | Entity id. Out-of-range ids are ignored. |
flags | number | TRANSFORM_ALL | Which lanes changed; defaults to position, rotation and scale. |
Returns
void
Nothing.
Example
ts
import { Transform, TRANSFORM_FLAGS, markMoved } from 'gameable';
Transform.y[e] = Transform.y[e] + 0.1;
markMoved(e, TRANSFORM_FLAGS.POSITION);maxEntities()
ts
function maxEntities(): number;The entity ceiling the built-in components are currently sized for.
Returns
number
The configured maximum.
Example
ts
import { maxEntities } from 'gameable';
console.log(maxEntities()); // 4096prefab()
ts
function prefab(spec): PrefabDef;Declare a prefab.
Safe at module scope: nothing is resolved until the first spawn.
Parameters
| Parameter | Type | Description |
|---|---|---|
spec | PrefabSpec | What the entity is made of. |
Returns
The prefab, ready to spawn.
Example
ts
import { prefab } from 'gameable';
export const Crate = prefab({
asset: 'crate',
body: { shape: 'box', dims: [0.5, 0.5, 0.5], kind: 'dynamic', mass: 20 },
});prefabRegistry()
ts
function prefabRegistry(): readonly PrefabDef[];Every prefab declared so far, in declaration order.
Returns
readonly PrefabDef[]
The registry, for tooling and tests.
quatFromYawPitch()
ts
function quatFromYawPitch(
out,
yaw,
pitch
): void;Write a yaw/pitch pair into a quaternion, in the engine's Y-up convention.
Parameters
| Parameter | Type | Description |
|---|---|---|
out | Quat | Quaternion to overwrite. |
yaw | number | Yaw in radians, around +Y. |
pitch | number | Pitch in radians, around the camera's local +X. |
Returns
void
Nothing; out is mutated.
readKeyBit()
ts
function readKeyBit(words, index): boolean;Read one bit out of an 8-word key bitset.
Parameters
| Parameter | Type | Description |
|---|---|---|
words | ArrayLike<number> | The bitset, exactly KEY_WORDS long. |
index | number | A bit index from keyIndex. |
Returns
boolean
True when the bit is set.
readSnapshot()
ts
function readSnapshot(rt, state): unknown;Restore a state previously produced by writeSnapshot from the same build.
Parameters
| Parameter | Type | Description |
|---|---|---|
rt | RuntimeState | The runtime to overwrite. |
state | ArrayLike<number> | The bytes. |
Returns
unknown
The user state from defineGame({ snapshot }), if any.
Throws
A GameError-shaped object when the magic or version does not match.
requireRuntime()
ts
function requireRuntime(): RuntimeState;The runtime currently executing.
Returns
The active runtime.
Throws
When called outside init, tick or shutdown.
resetBuiltinStores()
ts
function resetBuiltinStores(): void;Zero every built-in component array without changing its length.
Returns
void
Nothing.
resetPrefabRegistry()
ts
function resetPrefabRegistry(): void;Reset prefab numbering. Tests only — a game never calls this.
Returns
void
Nothing.
roomSeats()
ts
function roomSeats(definition): number | undefined;The seats a game declares: features.multiplayer.maxPlayers. Seats are ids 0..seats - 1. The guest makes exactly that many player slots and ignores (with one warning) a join or input past them; a room built by createEngineRoomGame refuses the next joiner with full. Both read this function, so the two cannot disagree.
Parameters
| Parameter | Type | Description |
|---|---|---|
definition | GameDefinition | The defineGame result. |
Returns
number | undefined
The seat count, DEFAULT_ROOM_SEATS for multiplayer: true, or undefined when the game does not declare features.multiplayer.
Throws
When maxPlayers is not a whole number from 1 to 4097.
Example
ts
import { defineGame, roomSeats } from 'gameable';
const game = defineGame({ features: { multiplayer: { maxPlayers: 6 } } });
roomSeats(game); // 6: seats 0 to 5roomSendHz()
ts
function roomSendHz(definition): number;The rows rate a game declares, checked: features.multiplayer.sendHz, from 1 to 60 per second (60 is the simulation rate, so more would repeat rows).
Parameters
| Parameter | Type | Description |
|---|---|---|
definition | GameDefinition | The defineGame result. |
Returns
number
The declared rate, or 20 when none is declared.
Throws
When sendHz is declared but is not a number from 1 to 60.
Example
ts
import { defineGame, roomSendHz } from 'gameable';
roomSendHz(defineGame({ features: { multiplayer: { sendHz: 10 } } })); // 10
roomSendHz(defineGame({ features: { multiplayer: true } })); // 20setActiveRuntime()
ts
function setActiveRuntime(next): RuntimeState | null;Install the runtime the facades resolve against.
Parameters
| Parameter | Type | Description |
|---|---|---|
next | RuntimeState | null | The runtime, or null when leaving a guest call. |
Returns
RuntimeState | null
The previously active runtime, so calls can nest.
setLogSink()
ts
function setLogSink(next): void;Point prelude console at the host logger.
The runtime calls this at the top of init. Any lines buffered before then are flushed in order, so module-scope logging is not silently lost.
Parameters
| Parameter | Type | Description |
|---|---|---|
next | LogSink | null | The sink, or null to go back to buffering. |
Returns
void
Nothing.
setRandomSource()
ts
function setRandomSource(next): void;Route Math.random at the seeded SDK generator.
The runtime calls this from init. Until then Math.random() throws with RANDOM_MESSAGE, which is far kinder than a game that silently replays the same "random" sequence on every instantiation.
Only effective in the component guest; see IS_COMPONENT_GUEST.
Parameters
| Parameter | Type | Description |
|---|---|---|
next | (() => number) | null | The seeded generator, or null to re-arm the guard. |
Returns
void
Nothing.
spawn()
ts
function spawn(
def,
position,
rotation?
): number;Instantiate a prefab.
Mints the entity id, writes the built-in components and queues the spawn / add-body / spawn-character commands the host needs.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
def | PrefabDef | undefined | A prefab from prefab(). |
position | Vec3 | undefined | World position. |
rotation | Quat | IDENTITY | World rotation, xyzw. Defaults to identity. |
Returns
number
The new entity id.
Example
ts
import { spawn } from 'gameable';
const crate = spawn(Crate, { x: 0, y: 2, z: -5 });utf8Decode()
ts
function utf8Decode(
b,
start?,
end?
): string;Decode UTF-8 bytes without TextDecoder.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
b | ArrayLike<number> | undefined | The bytes. Any ArrayLike<number> works, including the plain Array jco hands the guest. |
start | number | 0 | First byte to read. |
end? | number | undefined | One past the last byte to read; defaults to b.length. |
Returns
string
The decoded string.
utf8Encode()
ts
function utf8Encode(s): Uint8Array;Encode a JavaScript string as UTF-8 without TextEncoder.
Parameters
| Parameter | Type | Description |
|---|---|---|
s | string | The string to encode. |
Returns
Uint8Array
A freshly allocated UTF-8 byte array.
writeKeyBit()
ts
function writeKeyBit(
words,
index,
value
): void;Set or clear one bit in an 8-word key bitset, in place.
Parameters
| Parameter | Type | Description |
|---|---|---|
words | Uint32Array | The bitset, exactly KEY_WORDS long. |
index | number | A bit index from keyIndex. |
value | boolean | True to set the bit, false to clear it. |
Returns
void
Nothing; words is mutated.
writeSnapshot()
ts
function writeSnapshot(rt, userState?): Uint8Array;Serialise the whole guest state.
Parameters
| Parameter | Type | Description |
|---|---|---|
rt | RuntimeState | The runtime to serialise. |
userState? | unknown | Extra state from defineGame({ snapshot }). |
Returns
Uint8Array
A freshly allocated byte string. Opaque to the host.