Lua API reference
The runtime exposes a sandboxed Lua 5.3 environment via fengari. Entity scripts define lifecycle hooks (onStart, onUpdate(dt), onCollision(otherId)) and can call the globals below. self is the entity handle, set before each hook.
- Removed globals:
os,io,debug,loadfile,dofile. printis overridden to callgame.log.requireresolves only against the manifest’s module registry.- Rotations in Lua are in degrees.
Input
game.getInputAxis
InputFunction form of `input.getAxis`. Convenient for legacy shims and event callbacks.
| Name | Type | Description |
|---|---|---|
| axis | string | Axis identifier. |
input.getAxis
InputReturns the current value of a float input axis.
| Name | Type | Description |
|---|---|---|
| axis | string | Axis identifier (e.g. `"pointer.dragX"`, `"pointer.scrollY"`, `"gesture.palmX"`, `"gesture.palmY"`). |
local yaw = input.getAxis("pointer.dragX")
self:setRotation(0, yaw * 180, 0)input.isPressed
InputReturns true while an input button is held.
| Name | Type | Description |
|---|---|---|
| button | string | Button identifier. Platform-registered examples: keyboard (`"key.space"`, `"key.1"`..`"key.5"`, `"key.q"`, `"key.e"`, `"key.f"`, `"key.r"`), gestures (`"gesture.open_palm"`, `"gesture.point"`, `"gesture.thumbs_up"`, `"gesture.thumbs_down"`), or multiplayer event names routed from phone controllers. |
if input.isPressed("gesture.open_palm") then
game.playAnimation(self.id, "flap")
endEntity
game.destroy
EntityRemoves an entity from the scene and destroys its physics body.
| Name | Type | Description |
|---|---|---|
| id | string|EntityHandle | Entity ID or handle. |
game.destroy("enemy_42")game.findEntity
EntityLooks up an entity declared in the manifest by its ID.
| Name | Type | Description |
|---|---|---|
| id | string | Entity ID from `manifest.entities[].id`. |
local bird = game.findEntity("phoenix")
if bird then bird:setPosition(0, 1, 0) endgame.spawn
EntitySpawns a new entity at runtime. Overloaded: - First arg is a string → spawn a registered prefab/asset by name (2nd arg optional position). - First arg is a table → spawn a primitive with `{ mesh, id?, position? }`.
| Name | Type | Description |
|---|---|---|
| prefabOrOpts | string|object | Prefab name, or `{ mesh: "box"|"sphere"|"plane"|"cylinder"|"cone"|"torus", id?: string, position?: {x,y,z} }`. |
| position? | object | `{x, y, z}` spawn position when first arg is a prefab name. |
local enemy = game.spawn("goblin", { x = 0, y = 0, z = -2 })
local cube = game.spawn({ mesh = "box", position = { x = 1, y = 0, z = 0 } })Transform
game.getBounds
TransformReturns an entity's world-space axis-aligned bounding box.
| Name | Type | Description |
|---|---|---|
| id | string|EntityHandle | Entity ID or handle. |
local b = game.getBounds(self.id)
if b then game.log("height:", b.size_y) endgame.getPosition
TransformReturns an entity's current world position.
| Name | Type | Description |
|---|---|---|
| id | string|EntityHandle | Entity ID or handle. |
local p = game.getPosition(self.id)
if p then game.log(p.x, p.y, p.z) endgame.move
TransformSets an entity's world position.
| Name | Type | Description |
|---|---|---|
| id | string|EntityHandle | Entity ID or handle. |
| pos | object | `{x, y, z}` in world units. |
game.move(self.id, { x = 0, y = 1, z = 0 })game.rotate
TransformSets an entity's Euler rotation in **degrees**. Susceptible to gimbal lock — prefer `game.rotateQ` for accumulated rotations.
| Name | Type | Description |
|---|---|---|
| id | string|EntityHandle | Entity ID or handle. |
| rot | object | `{x, y, z}` Euler angles in **degrees**. |
game.rotate(self.id, { x = 0, y = 90, z = 0 })game.rotateQ
TransformSets an entity's rotation from a quaternion. No gimbal lock — preferred for frame-to-frame accumulated rotations.
| Name | Type | Description |
|---|---|---|
| id | string|EntityHandle | Entity ID or handle. |
| w | number | Quaternion w component. |
| x | number | Quaternion x component. |
| y | number | Quaternion y component. |
| z | number | Quaternion z component. |
game.rotateQ(self.id, 1, 0, 0, 0) -- identitygame.scale
TransformSets an entity's scale. Pass a single number for uniform scale, or `{x, y, z}` for per-axis scale.
| Name | Type | Description |
|---|---|---|
| id | string|EntityHandle | Entity ID or handle. |
| scale | number|object | Uniform multiplier, or `{x, y, z}`. |
game.scale(self.id, 1.5)
game.scale(self.id, { x = 1, y = 2, z = 1 })Physics
game.addPhysics
PhysicsAdds a dynamic cannon-es rigid body to the entity at runtime. If the entity already has a body this is a no-op.
| Name | Type | Description |
|---|---|---|
| id | string|EntityHandle | Entity ID or handle. |
| opts? | object | |
| opts.mass? | number = 1 | Rigid body mass (kg). |
| opts.gravity? | boolean = true | Whether gravity affects this body. |
game.addPhysics(self.id, { mass = 2, gravity = true })game.push
PhysicsApplies an instantaneous impulse to the entity's rigid body. Wakes sleeping bodies. No-op if the entity has no physics body.
| Name | Type | Description |
|---|---|---|
| id | string|EntityHandle | Entity ID or handle. |
| impulse | object | `{x, y, z}` impulse vector. |
game.push(self.id, { x = 0, y = 5, z = 0 }) -- jumpTween
game.cancelTween
TweenCancels any in-flight tween on the entity.
| Name | Type | Description |
|---|---|---|
| id | string|EntityHandle | Entity ID or handle. |
game.tween
TweenTweens any combination of entity properties to target values over a duration. Targets are a flat table keyed by property name — accepted keys include `position_x/y/z`, `rotation_x/y/z` (degrees), `scale_x/y/z`, and any entity property your `GameAPI` registers.
| Name | Type | Description |
|---|---|---|
| id | string|EntityHandle | Entity ID or handle. |
| targets | object | Flat table of `{ property_name = target_value, ... }`. |
| duration | number | Seconds. |
| easing? | string | Easing name (`"linear"`, `"easeInOutQuad"`, etc.). |
game.tween(self.id, { position_y = 2, rotation_y = 90 }, 0.5, "easeInOutQuad")Animation
game.listAnimations
AnimationReturns the list of animation clip names available on an entity's model. Returns an empty array for entities without a model or with no clips.
| Name | Type | Description |
|---|---|---|
| entityId | string | Entity ID. |
for i, name in ipairs(game.listAnimations(self.id)) do game.log(name) endgame.playAnimation
AnimationPlays an animation clip on an entity. Returns `true` if the clip exists.
| Name | Type | Description |
|---|---|---|
| entityId | string | Entity ID. |
| name | string | Clip name (see `game.listAnimations`). |
| opts? | object | |
| opts.loop? | boolean = true | Loop the clip. |
| opts.timeScale? | number = 1 | Playback rate multiplier. |
| opts.crossFadeDuration? | number = 0 | Seconds to crossfade from the current clip. |
game.playAnimation(self.id, "flap", { loop = true, crossFadeDuration = 0.2 })game.playAnimationOnce
AnimationPlays an animation clip exactly once (loop=false, no crossfade). Returns `true` if the clip exists.
| Name | Type | Description |
|---|---|---|
| entityId | string | Entity ID. |
| name | string | Clip name. |
game.stopAnimation
AnimationStops the entity's current animation (if any) and returns the model to its bind pose.
| Name | Type | Description |
|---|---|---|
| entityId | string | Entity ID. |
Audio
game.fadeOutAll
AudioFades out **all** music channels simultaneously. Useful for scene transitions.
| Name | Type | Description |
|---|---|---|
| duration | number | Fade-out length in seconds. |
game.fadeOutMusic
AudioFades a music channel's volume to zero over `duration` seconds, then stops it.
| Name | Type | Description |
|---|---|---|
| duration | number | Fade-out length in seconds. |
| channel? | string = "default" | Channel name. |
game.playMusic
AudioPlays a looping/long-running music track on a named channel. Music channels are independent: call `playMusic` with different `channel` strings to layer tracks. Use `game.setMusic` to adjust volume/pitch mid-playback.
| Name | Type | Description |
|---|---|---|
| name | string | Sound name from the manifest. |
| opts? | object | |
| opts.volume? | number = 1 | 0..1 |
| opts.loop? | boolean = true | |
| opts.pitch? | number = 1 | Playback rate / pitch multiplier. |
| opts.channel? | string = "default" | Channel name for later `stopMusic`/`setMusic`/`fadeOutMusic`. |
game.playMusic("theme", { channel = "bg", volume = 0.4 })game.playSound
AudioPlays a one-shot sound effect. Sound names map to assets via the manifest's `sounds` table.
| Name | Type | Description |
|---|---|---|
| name | string | Sound name from `manifest.sounds[].name`. |
| opts? | object | |
| opts.volume? | number = 1 | 0..1 |
| opts.loop? | boolean = false |
game.playSound("pop", { volume = 0.6 })game.setMusic
AudioAdjusts volume and/or pitch on a playing music channel without restarting it.
| Name | Type | Description |
|---|---|---|
| channel | string | Channel name. |
| opts? | object | |
| opts.volume? | number | |
| opts.pitch? | number |
game.setMusic("bg", { volume = 0.2, pitch = 1.2 })game.stopMusic
AudioImmediately stops music on the named channel.
| Name | Type | Description |
|---|---|---|
| channel | string | Channel name (use `"default"` if you omitted `channel` in `playMusic`). |
game.stopSound
AudioStops any currently-playing instances of the named sound.
| Name | Type | Description |
|---|---|---|
| name | string | Sound name. |
Timers
game.after
TimersRuns a callback once after `seconds`. Returns a timer ID you can cancel.
| Name | Type | Description |
|---|---|---|
| seconds | number | Delay in seconds. |
| callback | function | Lua function to call (no arguments). |
game.after(1.5, function() game.playSound("pop") end)game.cancelTimer
TimersCancels a timer returned by `game.after` or `game.every`. Unknown IDs are ignored.
| Name | Type | Description |
|---|---|---|
| timerId | number | ID returned by `game.after` or `game.every`. |
game.every
TimersRuns a callback repeatedly at `seconds` interval. Returns a timer ID.
| Name | Type | Description |
|---|---|---|
| seconds | number | Interval in seconds. |
| callback | function | Lua function to call (no arguments). |
local t = game.every(1, function() game.log("tick") end)
game.after(10, function() game.cancelTimer(t) end)Events
game.fireEvent
EventsFires a named event. All subscribers registered via `game.on(event, ...)` are called synchronously in registration order.
| Name | Type | Description |
|---|---|---|
| event | string | Event name. |
game.fireEvent("score", 10)game.on
EventsSubscribes a callback to a named event. Events can come from the runtime (collisions, gestures) or be fired from any script via `game.fireEvent`. For multiplayer-scoped events see `game.onPlayer`.
| Name | Type | Description |
|---|---|---|
| event | string | Event name. |
| callback | function | `function(...args)` called when the event fires. |
game.on("score", function(points) total = total + points end)Multiplayer
game.getActivePlayer
MultiplayerReturns the currently active player ID, set via `game.setActivePlayer`.
game.onPlayer
MultiplayerSubscribes to an event scoped to a specific player. Sugar for `game.on("p{playerId}_{event}", callback)`. Use with the multiplayer relay which dispatches phone-controller inputs as player-scoped events.
| Name | Type | Description |
|---|---|---|
| playerId | number | Player index (1-based). |
| event | string | Event name (without the `p{n}_` prefix). |
| callback | function | `function(...args)` called when the scoped event fires. |
game.onPlayer(1, "swing", function(power) hit(1, power) end)game.setActivePlayer
MultiplayerSets the active player ID used by gameplay systems that route input/state by current player (e.g. turn-based games).
| Name | Type | Description |
|---|---|---|
| playerId | number | Player index (1-based). |
UI (3D)
game.ui.remove
UI (3D)Removes a 3D UI text label.
| Name | Type | Description |
|---|---|---|
| id | string | Handle from `game.ui.text`. |
game.ui.text
UI (3D)Creates a 3D world-space text label. The label renders in the scene at the given coordinates and follows the camera orientation (billboarded).
| Name | Type | Description |
|---|---|---|
| text | string | Text to display (auto-coerced via `tostring`). |
| opts? | object | |
| opts.x? | number = 0 | |
| opts.y? | number = 0 | |
| opts.z? | number = 0 | |
| opts.size? | number = 1 | World-space text size. |
local h = game.ui.text("HP: 10", { y = 2, size = 0.5 })game.ui.update
UI (3D)Replaces the content of an existing 3D UI text label.
| Name | Type | Description |
|---|---|---|
| id | string | Handle from `game.ui.text`. |
| text | string | New text (auto-coerced via `tostring`). |
Side UI
game.side.get
Side UIReturns the currently active side set via `game.side.set`.
game.side.prefab
Side UIInstantiates a registered side-UI prefab (HUD/status/score widget) on a side of the Pepper's Ghost diamond.
| Name | Type | Description |
|---|---|---|
| side | string | `"front"`, `"back"`, `"left"`, or `"right"`. |
| name | string | Prefab name registered by the runtime or manifest. |
| opts? | object | |
| opts.anchor_x? | number = 0.5 | |
| opts.anchor_y? | number = 0.5 |
game.side.remove
Side UIRemoves a side overlay by its handle.
| Name | Type | Description |
|---|---|---|
| handle | string | ID returned by `game.side.text` or `game.side.prefab`. |
game.side.set
Side UISets the active side for the Pepper's Ghost diamond viewport. Subsequent calls that omit a `side` argument use this value.
| Name | Type | Description |
|---|---|---|
| side | string | `"front"`, `"back"`, `"left"`, or `"right"`. |
game.side.setColor
Side UISets the color of a named sub-element of a side prefab.
| Name | Type | Description |
|---|---|---|
| handle | string | Prefab handle from `game.side.prefab`. |
| path | string | Sub-element path within the prefab. |
| color | string | CSS color string. |
game.side.setFill
Side UIUpdates a fill ratio (0..1) on a named sub-element of a side prefab — e.g. a progress bar's "bar" path.
| Name | Type | Description |
|---|---|---|
| handle | string | Prefab handle from `game.side.prefab`. |
| path | string | Sub-element path within the prefab (e.g. `"bar"`). |
| ratio | number | 0..1 |
game.side.text
Side UIPlaces a 2D text overlay on one face of the Pepper's Ghost diamond. Overlays are screen-space per side — they stay put as the user looks around.
| Name | Type | Description |
|---|---|---|
| side | string | `"front"`, `"back"`, `"left"`, or `"right"`. |
| text | string | Text to display (auto-coerced via `tostring`). |
| opts? | object | |
| opts.size? | number | Font size. |
| opts.color? | string | CSS color string. |
| opts.anchor_x? | number = 0.5 | 0 = left, 0.5 = center, 1 = right. |
| opts.anchor_y? | number = 0.5 | 0 = top, 0.5 = center, 1 = bottom. |
local h = game.side.text("front", "Hello", { size = 48, anchor_y = 0.1 })Scene
game.clearScene
SceneDestroys all runtime-spawned entities. Entities declared in the manifest are preserved.
game.loadScene
SceneRequests a scene transition. The client handles the actual navigation — typically maps `name` to a manifest and loads it.
| Name | Type | Description |
|---|---|---|
| name | string | Scene identifier. |
game.loadScene("main_menu")Viewport
game.setBloom
ViewportAdjusts bloom post-processing at runtime. Pass any subset of `{ intensity, threshold, radius }`; omitted keys keep their current value.
| Name | Type | Description |
|---|---|---|
| opts? | object | |
| opts.intensity? | number | Overall bloom strength. |
| opts.threshold? | number | Luminance threshold above which pixels bloom. |
| opts.radius? | number | Bloom spread radius. |
game.setBloom({ intensity = 1.5, threshold = 0.7 })game.setCameraDistance
ViewportSets the camera distance for the Pepper's Ghost viewport. No effect in standard viewport mode.
| Name | Type | Description |
|---|---|---|
| distance | number | World-space distance from target. |
VFX
game.vfx
VFXSpawns a one-shot visual effect at a world position. VFX names are registered by the runtime (e.g. sparkles, explosions).
| Name | Type | Description |
|---|---|---|
| name | string | VFX name. |
| pos | object | `{x, y, z, scale?}`. `scale` multiplies the default VFX size. |
game.vfx("sparkle", { x = 0, y = 1, z = 0, scale = 2 })Mesh
game.cut
MeshSlices an entity's mesh along a plane, producing two new entities. The plane is defined by a normal `{nx, ny, nz}` and an optional point `{px, py, pz}` (defaults to entity center). The original entity is destroyed.
| Name | Type | Description |
|---|---|---|
| id | string|EntityHandle | Entity ID or handle. |
| plane | object | `{ nx, ny, nz, px?, py?, pz? }`. Normal defaults to `{1,0,0}`; point defaults to entity center. |
local pieces = game.cut(self.id, { nx = 0, ny = 1, nz = 0 })
if pieces then pieces.pos:applyImpulse(0, 2, 0) endSettings
game.getSetting
SettingsReads a key previously stored via `game.setSetting`.
| Name | Type | Description |
|---|---|---|
| key | string | Setting name. |
game.setSetting
SettingsStores a key/value in the runtime settings bag. Used for cross-script communication and manifest-level tuning knobs.
| Name | Type | Description |
|---|---|---|
| key | string | Setting name. |
| value | boolean|number|string | Value (non-primitives are ignored). |
Logging
game.clock
LoggingSeconds since the Unix epoch — useful as a seed for `math.randomseed`.
math.randomseed(game.clock())game.log
LoggingWrites any number of arguments to the in-UI debug console. The global `print(...)` is rewired to call this.
| Name | Type | Description |
|---|---|---|
| args | ...any | Values to log. Numbers, strings, booleans stringified. |
game.log("hp:", hp, "pos:", self:getPosition())Standard Lua `print` is overridden to route through `game.log`, so all output appears in the runtime's in-UI debug console.
| Name | Type | Description |
|---|---|---|
| args | ...any |
Entity handle (self)
self:applyForce
Entity handle (self)Applies a continuous force to the entity's rigid body (ignored if no physics body). Must be called every frame for sustained motion. Wakes sleeping bodies.
| Name | Type | Description |
|---|---|---|
| x | number | |
| y | number | |
| z | number |
self:applyImpulse
Entity handle (self)Applies an instantaneous impulse to the entity's rigid body. Wakes sleeping bodies. Use for jumps, knockbacks, projectile launches.
| Name | Type | Description |
|---|---|---|
| x | number | |
| y | number | |
| z | number |
self:destroy
Entity handle (self)Destroys this entity.
self:destroy()self:getPosition
Entity handle (self)Returns the entity's world position as three separate numbers (not a table).
local x, y, z = self:getPosition()self:getRotation
Entity handle (self)Returns the entity's Euler rotation in **degrees** as three separate numbers.
self:getScale
Entity handle (self)Returns the entity's scale as three separate numbers.
self:getVelocity
Entity handle (self)Returns the entity's current rigid-body velocity as three numbers. Returns `(0, 0, 0)` if the entity has no physics body.
self:setPosition
Entity handle (self)Sets the entity's world position.
| Name | Type | Description |
|---|---|---|
| x | number | |
| y | number | |
| z | number |
self:setPosition(0, 1, 0)self:setRotation
Entity handle (self)Sets the entity's Euler rotation in **degrees**. Prefer `game.rotateQ` for accumulated frame-to-frame rotations (avoids gimbal lock).
| Name | Type | Description |
|---|---|---|
| x | number | Degrees. |
| y | number | Degrees. |
| z | number | Degrees. |
self:setScale
Entity handle (self)Sets the entity's per-axis scale.
| Name | Type | Description |
|---|---|---|
| x | number | |
| y | number | |
| z | number |
self:setVelocity
Entity handle (self)Directly sets the entity's rigid-body velocity. Ignored if no physics body.
| Name | Type | Description |
|---|---|---|
| x | number | |
| y | number | |
| z | number |
self:tween
Entity handle (self)Tweens a single named property on this entity. For multi-property tweens prefer `game.tween(self.id, {...}, duration, easing)`.
| Name | Type | Description |
|---|---|---|
| property | string | Property name (e.g. `"position"`, `"rotation"`, `"scale"`). |
| target | object | `{x=, y=, z=}` target values. |
| duration | number | Seconds. |
| easing? | string |
self:tween("position", { x = 0, y = 2, z = 0 }, 0.5, "easeInOutQuad")