Script API Reference
Complete reference for the ScriptAPI object. Access transforms, physics, input, audio, and more from your scripts.
Entity Information#
api.eid#
The entity ID this script is attached to. Read-only.
// Type: number (read-only)const myEntityId = api.eid;api.log(`I am entity ${myEntityId}`);Transform#
api.position#
World position of the entity as a Vector3. Read/write.
// Type: THREE.Vector3 (pooled) // Read positionconst pos = api.position;console.log(pos.x, pos.y, pos.z); // Modify and write backpos.y += 1.0;api.position = pos; // Set directlyapi.position = { x: 0, y: 10, z: 0 };Pooled Object
api.position returns a pooled Vector3. Don't store references between frames — copy values if needed.
api.rotation#
World rotation as Euler angles (radians). Read/write.
// Type: THREE.Euler (pooled) // Rotate 45 degrees around Yconst rot = api.rotation;rot.y = Math.PI / 4;api.rotation = rot; // Increment rotationrot.y += 0.01;api.rotation = rot;api.quaternion#
World rotation as a quaternion. Read/write. Quaternions are more robust than Euler angles for rotation math.
// Type: THREE.Quaternion (pooled) // Set quaternion directlyapi.quaternion = { x: 0, y: 0.7071, z: 0, w: 0.7071 }; // Automatically syncs with api.rotationconst rot = api.rotation; // Updated automaticallyapi.log(rot.y); // Shows equivalent Euler angleapi.lookAt(target)#
Rotate the entity to face a target point.
// Look at a world positionapi.lookAt({ x: 10, y: 0, z: 10 }); // Look at another entity's positionif (api.target.exists) { api.lookAt(api.target.position);}Physics#
Physics methods require a RigidBody component on the entity.
api.velocity#
Linear velocity of the physics body. Read/write.
// Type: THREE.Vector3 (pooled) // Read current velocityconst vel = api.velocity;api.log(`Speed: ${vel.length()}`); // Set velocity directlyapi.velocity = { x: 0, y: 5, z: 10 };api.applyImpulse(force)#
Apply an instantaneous force impulse to the physics body.
// Jump impulseapi.applyImpulse({ x: 0, y: 10, z: 0 }); // Knockbackapi.applyImpulse({ x: -5, y: 2, z: 0 });api.raycast(origin, direction)#
Cast a ray and return the first hit. Returns a Promise.
// Type: Promise<RaycastResult | null> interface RaycastResult { eid: number; // Entity ID hit distance: number; // Distance from origin point: [number, number, number]; // Hit point normal: [number, number, number]; // Surface normal} // Ground checkconst hit = await api.raycast( api.position, { x: 0, y: -1, z: 0 }); if (hit && hit.distance < 1.1) { api.log("On ground!");} // Forward checkconst forwardHit = await api.raycast( api.position, { x: 0, y: 0, z: -1 });Character Controller#
Character controller methods require a CharacterController component.
api.controller.move(velocity)#
Move the character with collision handling.
// Move based on inputconst { move } = api.input;api.controller.move({ x: move.x * speed * dt, y: 0, z: move.y * speed * dt});api.controller.isGrounded#
Check if the character is on the ground. Read-only.
// Type: boolean (read-only) if (api.controller.isGrounded) { // Can jump if (api.input.jumpDown) { api.applyImpulse({ x: 0, y: jumpForce, z: 0 }); }}Navigation#
Navigation methods require a NavMeshAgent component. The navigation system uses A* pathfinding on a navigation mesh.
api.navigation#
interface Navigation { setDestination(target: Vector3): void; stop(): void; setSpeed(speed: number): void; readonly isMoving: boolean; readonly isPathPending: boolean;} // Set destinationapi.navigation.setDestination({ x: 10, y: 0, z: 10 }); // Stop movingapi.navigation.stop(); // Change speed (affects both NavMeshAgent and Steering)api.navigation.setSpeed(5.0); // Check stateif (api.navigation.isMoving) { api.log("Agent is moving");} if (api.navigation.isPathPending) { api.log("Path calculation in progress");}Input#
Access keyboard, mouse, and gamepad input state.
api.input#
interface InputState { move: Vector2; // WASD: x = left/right, y = back/forward look: Vector2; // Mouse delta jump: boolean; // Jump key held jumpDown: boolean; // Jump pressed this frame sprint: boolean; // Sprint key held} // Movementconst { move, jumpDown, sprint } = api.input; const speed = sprint ? 10.0 : 5.0; if (move.x !== 0 || move.y !== 0) { api.controller.move({ x: move.x * speed * dt, y: 0, z: move.y * speed * dt });} // Mouse lookconst { look } = api.input;const rot = api.rotation;rot.y -= look.x * 0.002;api.rotation = rot; // Jump (edge trigger)if (jumpDown && api.controller.isGrounded) { api.applyImpulse({ x: 0, y: 10, z: 0 });}Animation#
Control skeletal animations. Requires an Animation component.
api.animation#
interface AnimationController { play(clipIndexOrName: number | string, loop?: boolean, speed?: number): void; crossFade(clipIndexOrName: number | string, duration: number): void; stop(): void; playSecondary(clipIndexOrName: number | string, maskBoneNameHash: number, weight?: number): void; stopSecondary(): void;} // Play animation by indexapi.animation.play(0, true, 1.0); // Play by nameapi.animation.play("Walk", true); // Cross-fade to another animationapi.animation.crossFade("Run", 0.3); // Stop animationapi.animation.stop();Audio#
Control audio playback. Requires an AudioSource component.
api.audio#
interface AudioController { play(assetId?: number): void; stop(): void; setVolume(volume: number): void;} // Play the assigned audioapi.audio.play(); // Play a specific audio assetapi.audio.play(123); // Stop playbackapi.audio.stop(); // Adjust volume (0.0 - 1.0)api.audio.setVolume(0.5);Target#
Access a target entity's transform. Requires a Target component.
api.target#
interface TargetAccessor { readonly exists: boolean; readonly position: THREE.Vector3; readonly rotation: THREE.Euler;} // Check if target existsif (api.target.exists) { // Follow target const targetPos = api.target.position; api.lookAt(targetPos); // Move toward target const myPos = api.position; const dx = targetPos.x - myPos.x; const dz = targetPos.z - myPos.z; const distance = Math.sqrt(dx*dx + dz*dz); if (distance > 2.0) { myPos.x += (dx / distance) * speed * dt; myPos.z += (dz / distance) * speed * dt; api.position = myPos; }}Networking#
Network state for multiplayer games. Requires a Networked component.
api.network#
interface NetworkState { readonly isServer: boolean; readonly isLocalPlayer: boolean; readonly ownerId: number;} // Only process input for local playerif (!api.network.isLocalPlayer) { return; // Skip for remote players} // Server-only logicif (api.network.isServer) { // Authoritative game logic} // Check ownershipif (api.network.ownerId === localClientId) { // This client owns this entity}Spawning & Scenes#
api.spawn(prefabId, position?, rotation?)#
Instantiate a prefab. Returns a Promise with the new entity ID.
// Spawn a prefab at a positionconst eid = await api.spawn("Bullet", { x: 0, y: 1, z: 0 });api.log(`Spawned entity ${eid}`); // Spawn with rotationconst enemyEid = await api.spawn("Enemy", { x: 10, y: 0, z: 10 }, { x: 0, y: Math.PI, z: 0 });api.loadScene(url, additive?)#
Load a scene file. Returns a Promise.
// Load a new scene (replaces current)await api.loadScene("/scenes/level2.json"); // Load additively (adds to current scene)await api.loadScene("/scenes/enemies.json", true);Debug#
api.log(message)#
Log a message to the console and debug panel.
api.log("Hello from script!");api.log(`Position: ${api.position.x}, ${api.position.y}, ${api.position.z}`);TypeScript Definitions#
interface ScriptAPI { readonly eid: number; position: THREE.Vector3; rotation: THREE.Euler; velocity: THREE.Vector3; lookAt(target: THREE.Vector3): void; applyImpulse(force: THREE.Vector3): void; raycast(origin: THREE.Vector3, direction: THREE.Vector3): Promise<RaycastResult | null>; controller: CharacterControllerAPI; navAgent: NavAgentAPI; input: InputState; animation: AnimationAPI; audio: AudioAPI; target: TargetAPI; network: NetworkState; spawn(prefabId: string, position?: THREE.Vector3, rotation?: THREE.Euler): Promise<number>; loadScene(url: string, additive?: boolean): Promise<void>; log(message: string): void;}