Web Engine Docs
Preparing documentation
Use the search bar to quickly find any topic
Preparing documentation
Use the search bar to quickly find any topic
Complete reference for the ScriptAPI object. Access transforms, physics, input, audio, and more from your scripts.
The entity ID this script is attached to. Read-only.
// Type: number (read-only)const myEntityId = api.eid;api.log(`I am entity ${myEntityId}`);
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.
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;
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 angle
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 methods require a RigidBody component on the entity.
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 };
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 });
Cast a ray and return the first hit. Returns a Promise.
// Type: Promise<RaycastResult | null>interface RaycastResult {eid: number; // Entity ID hitdistance: number; // Distance from originpoint: [number, number, number]; // Hit pointnormal: [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 methods require a CharacterController component.
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});
Check if the character is on the ground. Read-only.
// Type: boolean (read-only)if (api.controller.isGrounded) {// Can jumpif (api.input.jumpDown) {api.applyImpulse({ x: 0, y: jumpForce, z: 0 });}}
Navigation methods require a NavMeshAgent component. The navigation system uses A* pathfinding on a navigation mesh.
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");}
Access keyboard, mouse, and gamepad input state.
interface InputState {move: Vector2; // WASD: x = left/right, y = back/forwardlook: Vector2; // Mouse deltajump: boolean; // Jump key heldjumpDown: boolean; // Jump pressed this framesprint: 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 });}
Control skeletal animations. Requires an Animation component.
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();
Control audio playback. Requires an AudioSource component.
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);
Access a target entity's transform. Requires a Target component.
interface TargetAccessor {readonly exists: boolean;readonly position: THREE.Vector3;readonly rotation: THREE.Euler;}// Check if target existsif (api.target.exists) {// Follow targetconst targetPos = api.target.position;api.lookAt(targetPos);// Move toward targetconst 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;}}
Network state for multiplayer games. Requires a Networked component.
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}
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 });
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);
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}`);
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;}