Files
gta6/three-player-controller/README_En.md
knsoandClaude Opus 5 fb38652e7f chore(controller): three-player-controller als Ordner statt Submodul
Das Submodul zeigte auf GitHub, der eingetragene Stand 028c652 lag aber nur
lokal vor: GitHub-Master ac39682 plus Showcase mit Figuren und launch.json.
Frische Klone konnten den Controller deshalb nicht laden. Jetzt liegt dieser
Stand als normaler Ordner im Repo, ohne Verweis auf GitHub.

dist/ und node_modules/ im Controller bleiben ignoriert und werden wie bisher
lokal gebaut.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 21:27:03 +02:00

51 KiB
Raw Permalink Blame History

中文 | English

three-player-controller

NPM Package Github CesiumJS

A lightweight first-person / third-person player controller for three.js. It provides character capsule collision, animation, camera obstacle avoidance, moving platforms, dynamic rigid bodies, vehicle control, and Foot IK, with three-mesh-bvh accelerating collision queries in complex scenes.

Features

  • First-person / third-person switching
  • Walking, running, jumping, and flying
  • Character capsule collision and ground detection
  • Camera obstacle avoidance, over-shoulder view, and spring camera
  • Static / kinematic / dynamic colliders
  • Dynamic spheres, boxes, and rigid-body collision
  • Asynchronous BVH construction with Worker
  • Streaming collider construction for 3D Tiles
  • Built-in vehicle physics, suspension, steering, grip, and handbrake
  • Foot IK plugin
  • Custom animations and Locomotion Sets
  • Mobile virtual joystick and buttons
  • Custom key bindings and external input

Examples

Online Demo

Installation

npm install three-player-controller three-mesh-bvh

Run Locally

git clone https://github.com/hh-hang/three-player-controller.git
cd three-player-controller
npm install
npm run dev

Open http://localhost:5173/three-player-controller/ in your browser.

Quick Start

import * as THREE from "three";                                                
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";       
import { playerController } from "three-player-controller"; 

const scene = new THREE.Scene();                                           

const camera = new THREE.PerspectiveCamera(
    60,                                                                      
    window.innerWidth / window.innerHeight,                                   
    0.1,                                                                  
    1000,
);

const renderer = new THREE.WebGLRenderer();                                    
renderer.setSize(window.innerWidth, window.innerHeight);                       
document.body.appendChild(renderer.domElement);                                

const controls = new OrbitControls(camera, renderer.domElement);        

// Load the player model
const loader = new GLTFLoader();                                              
const playerGltf = await loader.loadAsync("./glb/player.glb");

// Create the player controller
const player = new playerController();     

// Initialize player control
await player.init({
    scene,                                  // three.js scene instance
    camera,                                 // three.js camera instance
    controls,                               // external camera controller

    // Player model, animation, and movement settings
    playerModelConfig: {                                                       
        model: playerGltf.scene,            // root node of the externally loaded model
        animations: playerGltf.animations,  // animation clips
        scale: 0.001,                       // model scale
        idleAnim: "idle",                   // idle animation name
        walkAnim: "walk",                   // walk animation name
        runAnim: "run",                     // run animation name
        jumpAnim: "jump",                   // jump animation name; or ["start", "loop", "land"] for three-stage playback
    },

    // Initial player spawn point
    initPos: new THREE.Vector3(0, 0, 0),                                       

    // Colliders registered during initialization
    colliders: [                                                               
        {
            motion: "static",               // static collider
            shape: {                        // collision shape settings
                kind: "mesh",               // use a Mesh collision shape
                source: scene,              // collect geometry from this object and build the BVH automatically
            },
        },
    ],
});

function animate() {
    requestAnimationFrame(animate);                                            
    player.update();                                                          
    renderer.render(scene, camera);                                          
}

animate();                                                                  

player.update() already drives the camera controller passed to the player. Do not call controls.update() again in the render loop, or it may conflict with the internal camera logic.

Complete Parameter Example

init()

await player.init({
    // Required
    scene,    // three.js scene instance
    camera,   // three.js camera instance
    controls, // external camera controller, usually OrbitControls

    playerModelConfig: {                              // player model, animation, and movement settings
        model: playerGltf.scene,                      // root node of the externally loaded model
        animations: playerGltf.animations,            // animation clips
        scale: 0.001,                                  // model scale
        idleAnim: "idle",                              // idle animation name
        walkAnim: "walk",                              // walk animation name
        runAnim: "run",                                // run animation name
        jumpAnim: "jump",                              // jump animation name; or ["start", "loop", "land"] for three-stage playback

        // Directional / flight animations (optional; corresponding defaults are reused when omitted)
        leftWalkAnim: "leftWalk",                      // left-strafe animation; defaults to walkAnim
        rightWalkAnim: "rightWalk",                    // right-strafe animation; defaults to walkAnim
        backwardAnim: "walkBack",                      // backward animation; defaults to walkAnim
        flyAnim: "fly",                                // flight animation; defaults to idleAnim
        flyIdleAnim: "flyIdle",                        // fly-idle animation; defaults to idleAnim
        flyHoverForwardAnim: "flyFwd",                 // forward hover animation; defaults to flyAnim
        flyHoverBackAnim: "flyBack",                   // backward hover animation; defaults to flyIdleAnim
        flyHoverLeftAnim: "flyLeft",                   // left hover animation; defaults to flyIdleAnim
        flyHoverRightAnim: "flyRight",                 // right hover animation; defaults to flyIdleAnim
        flyHoverUpAnim: "flyUp",                       // upward hover animation; defaults to flyIdleAnim
        flyHoverDownAnim: "flyDown",                   // downward hover animation; defaults to flyIdleAnim
        drivingAnim: "driving",                        // driving loop animation; defaults to idleAnim

        // Physics settings (optional)
        gravity: -2400,                                // base gravity value, scaled by scale; default -2400
        jumpHeight: 600,                               // base jump-height value, scaled by scale; default 600
        speed: 200,                                    // base movement speed, scaled by scale; default 200
        runSpeed: 600,                                 // base run speed, scaled by scale; default 600
        flySpeed: 2100,                                // base flight speed, scaled by scale; default 2100
        acceleration: 30,                              // XZ acceleration response; default 30
        deceleration: 30,                              // XZ deceleration response; default 30

        // Model settings (optional)
        rotateY: 0,                                    // initial player facing direction in radians; default 0
        headBoneName: "Head",                          // head bone name used for first-person camera attachment
        firstPersonCameraOffset: [0, 40, 30],          // local first-person camera offset
        capsuleRadiusRatio: 1,                         // capsule radius multiplier; default 1
    },

    // Scene and collision (optional)
    initPos: new THREE.Vector3(0, 0, 0),                // initial spawn point; default (0, 0, 0)
    colliders: [                                        // colliders created during initialization; call addCollider() after init() if omitted
        {
            motion: "static",                           // collider motion type: static / kinematic / dynamic
            shape: {                                    // collision shape settings
                kind: "mesh",                           // Mesh collision shape
                source: world,                          // collision source; THREE.Object3D or THREE.Object3D[]
            },
            useWorker: false,                           // whether to build the BVH asynchronously in a Worker
        },
    ],

    // Camera (optional)
    minCamDistance: 100,                                // minimum third-person camera distance; default 100
    maxCamDistance: 440,                                // maximum third-person camera distance; default 440
    camLookAtHeightRatio: 0.8,                          // camera target height ratio, 0=bottom and 1=top; default 0.8
    thirdMouseMode: 1,                                  // third-person mouse mode 0-5; default 1
    enableZoom: false,                                  // whether mouse-wheel zoom is enabled; default false
    enableOverShoulderView: false,                      // whether over-shoulder view is enabled; default false
    camOverShoulderOffsetRatio: 0.2,                    // over-shoulder horizontal offset ratio; default 0.2
    isFirstPerson: false,                               // whether to start in first-person; default false
    enableSpringCamera: false,                          // whether spring camera is enabled; default false
    springCameraTime: 0.05,                             // spring-camera smoothing time in seconds; default 0.05

    // Other settings (optional)
    mouseSensitivity: 5,                                // mouse sensitivity; default 5
    timeScale: 1,                                       // time scale; <1 slow motion, >1 fast-forward; default 1

    keyMap: {                                           // custom key map; use one key, multiple keys, or null to disable
        forward: ["KeyW", "ArrowUp"],                   // move forward
        backward: ["KeyS", "ArrowDown"],                // move backward
        left: ["KeyA", "ArrowLeft"],                    // move left
        right: ["KeyD", "ArrowRight"],                  // move right
        sprint: ["ShiftLeft", "ShiftRight"],            // sprint; handbrake while driving
        jump: ["Space"],                                // jump; brake while driving
        toggleView: ["KeyV"],                           // switch first-person / third-person
        toggleFly: ["KeyF"],                            // toggle flight mode
        toggleVehicle: ["KeyE"],                        // enter / exit vehicle
    },

    isShowMobileControls: true,                         // whether to show virtual controls on mobile; default true
    mobileControls: {                                   // mobile button visibility, icons, and layout
        joystick: true,                                 // whether to show the virtual joystick; default true
        jump: {                                         // jump / brake button; pass false to hide
            icon: "/icons/custom-jump.svg",             // custom image URL for jump state
            brakeIcon: "/icons/custom-brake.svg",       // custom image URL for braking in vehicle mode
            right: 24,                                  // distance from the right edge in px
            bottom: 32,                                 // distance from the bottom edge in px
            size: 64,                                   // button diameter in px; default 56
        },
        fly: true,                                      // flight button; true uses default style, false hides it
        view: true,                                     // view-switch button; true uses default style, false hides it
        vehicle: true,                                  // enter / exit vehicle button; true uses default style, false hides it
    },
});

Collision System

Uses the unified CollisionWorld and addCollider() API.

A collider is described by two core properties:

{
    motion: "static" | "kinematic" | "dynamic",
    shape: {
        kind: "mesh" | "box" | "sphere",
        // ...
    }
}

Static Mesh

Suitable for terrain, buildings, walls, and other scene geometry that does not move.

const handle = player.addCollider({
    motion: "static",
    shape: {
        kind: "mesh",
        source: world,
    },
    useWorker: true,
});

Kinematic Mesh

Suitable for elevators, moving platforms, rotating platforms, vehicle visuals, or other objects whose transforms are controlled by application code.

const platformCollider = player.addCollider({
    motion: "kinematic",
    shape: {
        kind: "mesh",
        source: platform,
    },
    follow: platform,
    useWorker: true,
});

The collider follows follow.matrixWorld every frame. If you only need to adjust already-baked kinematic collision geometry at runtime, the BVH does not need to be rebuilt.

Dynamic Rigid Bodies

Dynamic box and sphere colliders automatically create built-in rigid bodies. DynamicBodySystem handles gravity, damping, contacts, impulses, and sleeping.

Dynamic Box

const box = new THREE.Mesh(
    new THREE.BoxGeometry(1, 1, 1),
    new THREE.MeshStandardMaterial(),
);
scene.add(box);

player.addCollider({
    motion: "dynamic",
    shape: {
        kind: "box",
        halfExtents: new THREE.Vector3(0.5, 0.5, 0.5),
        position: new THREE.Vector3(0, 5, 0),
    },
    mesh: box,
});

Dynamic Sphere

const sphere = new THREE.Mesh(
    new THREE.SphereGeometry(0.5),
    new THREE.MeshStandardMaterial(),
);
scene.add(sphere);

player.addCollider({
    motion: "dynamic",
    shape: {
        kind: "sphere",
        radius: 0.5,
        position: new THREE.Vector3(0, 5, 0),
    },
    mesh: sphere,
});

Dynamic rigid bodies can collide with static meshes, kinematic meshes, the player, vehicles, and other dynamic rigid bodies.

Remove And Clear

const handle = player.addCollider({
    motion: "static",
    shape: { kind: "mesh", source: object },
});

player.removeCollider(handle);

// Clear only static colliders
player.clearColliders({ motion: "static" });

// Clear all colliders
player.clearColliders();

Collision Groups

import { CollisionGroup } from "three-player-controller";
Group Description
CollisionGroup.DEFAULT Default scene collision group.
CollisionGroup.CHARACTER Player character collision group.
CollisionGroup.VEHICLE Vehicle collision group.
CollisionGroup.DEBRIS Dynamic debris, pushable objects, and similar colliders.
CollisionGroup.ALL Combination of all collision groups, allowing collision with every group.

By default, you do not need to set these values. The appropriate collision group and mask are selected automatically based on motion. You can also customize collision filtering with groups and mask:

player.addCollider({
    motion: "dynamic",
    shape: {
        kind: "sphere",
        radius: 0.5,
        position: new THREE.Vector3(),
    },
    groups: CollisionGroup.DEBRIS,
    mask: CollisionGroup.DEFAULT | CollisionGroup.VEHICLE,
});

Streaming 3D Tiles Collision

For 3D Tiles, it is not recommended to merge the entire Tileset into a single collider at once.

example/3dtilesScene.js uses a streaming strategy:

  1. Listen for tile load, unload, and visibility changes.
  2. Track only currently loaded tiles.
  3. Create colliders only for visible tiles near the player.
  4. Build BVHs asynchronously with useWorker: true.
  5. Immediately call removeCollider() when a tile is hidden, unloaded, or too far away.
  6. Use separate enter / exit radii to avoid frequent collider creation and destruction near the boundary.

Core pattern:

const handle = player.addCollider({
    motion: "kinematic",
    useWorker: true,
    shape: {
        kind: "mesh",
        source: tileScene,
    },
    follow: tileScene,
});

// Remove the collider when the tile unloads, becomes invisible, or is too far away
player.removeCollider(handle);

The example also demonstrates a local coordinate system and rebasing for globe-scale large-coordinate scenes. When the player moves too far from the local origin, the local coordinate system is updated while the player, camera, and velocity are transformed together to avoid floating-point precision issues at large coordinates.

See the full implementation in example/3dtilesScene.js.

Vehicle

const vehicleGltf = await loader.loadAsync("./glb/vehicle.glb"); // Load the vehicle model externally

const vehicle = await player.loadVehicleModel({
    // Required
    model: vehicleGltf.scene,                             // root node of the externally loaded vehicle model
    position: new THREE.Vector3(0, 0, 0),                 // initial vehicle world position
    wheelsNames: [                                       // wheel node names, order: front-left, front-right, rear-left, rear-right
        "Wheel_LF",                                      // front-left wheel node name
        "Wheel_RF",                                      // front-right wheel node name
        "Wheel_LR",                                      // rear-left wheel node name
        "Wheel_RR",                                      // rear-right wheel node name
    ],
    driverSeatPosition: new THREE.Vector3(-0.6, 0.7, 0.4), // driver-seat capsule center in chassis-local coordinates

    // Model / driving (optional)
    scale: 0.1,                                          // vehicle model scale; default 1
    driverSeatRotation: 0,                               // driver-seat horizontal rotation relative to chassis local space in radians; default 0
    modelRotation: -Math.PI / 2,                         // vehicle model Y-axis rotation in radians; default -π/2
    followVehicleDirection: true,                        // whether the camera follows vehicle heading while driving; default true

    // Debug (optional)
    debug: {                                             // vehicle physics debug visualization settings
        showPhysicsBox: false,                           // whether to show the chassis physics collision box; default false
        showWheelRays: false,                            // whether to show wheel suspension rays; default false
        showWheelTravel: false,                          // whether to show wheel Y-axis travel range; default false
        showWheelSpheres: false,                         // whether to show dynamic wheel collision spheres; default false
    },

    // Chassis (optional)
    chassis: {                                           // chassis collision box and damping settings
        density: 1,                                      // chassis density; mass is calculated from collision-box volume × density; default 1
        linearDamping: 0.05,                             // linear damping; default 0.05
        angularDamping: 0.5,                             // angular damping; default 0.5
        clearance: 0.15,                                 // height of the chassis box bottom relative to tire contact points; auto-calculated if omitted
        sizeScale: {                                     // chassis collision-box size multiplier relative to the automatic AABB
            x: 1,                                        // X-axis size multiplier; default 1
            y: 1,                                        // Y-axis size multiplier; default 1
            z: 1,                                        // Z-axis size multiplier; default 1
        },
    },

    // Suspension / tires (optional)
    suspension: {                                        // suspension and tire settings
        restLength: 0.3,                                 // suspension rest length; auto-calculated if omitted
        maxTravel: 0.35,                                 // maximum suspension travel; default 0.35
        stiffness: 18,                                   // suspension stiffness; default 18
        compression: 2.1,                                // suspension compression damping; default 2.1
        relaxation: 2.5,                                 // suspension rebound damping; default 2.5
        maxForce: 6000,                                  // maximum suspension force per wheel; default 6000
        frictionSlip: 8,                                 // longitudinal tire grip; default 8
        sideFrictionStiffness: 1,                        // lateral tire friction stiffness; default 1
        rollInfluence: 0.12,                             // effect of lateral force on body roll; default 0.12
    },

    // Steering (optional)
    steering: {                                          // steering settings
        maxSteerAngle: Math.PI / 5,                      // maximum steering angle in radians; default π/5
        steerTime: 0.45,                                 // time from center to full steering lock in seconds; default 0.45
        steerReturnTimeSlow: 0.55,                       // low-speed steering return time in seconds; default 0.55
        steerReturnTimeFast: 0.4,                        // high-speed steering return time in seconds; default 0.4
        highSpeedSteerScale: 0.3,                        // maximum steering ratio at top speed; default 0.3
    },

    // Grip / handbrake (optional)
    grip: {                                              // cornering grip and handbrake settings
        maxG: 1.2,                                       // maximum lateral acceleration in g; default 1.2
        sideFrictionIdle: 1,                             // lateral friction while driving straight; default 1
        sideFrictionFrontMin: 0.55,                      // minimum front-wheel lateral friction under high load; default 0.55
        sideFrictionRearMin: 0.45,                       // minimum rear-wheel lateral friction under high load; default 0.45
        handbrakeRearFriction: 0.35,                     // rear-wheel lateral friction while handbraking; default 0.35
        handbrakeRearDriveScale: 0.65,                   // rear-wheel drive-force ratio while handbraking; default 0.65
        handbrakeReleaseTime: 0.15,                      // recovery time after releasing the handbrake in seconds; default 0.15
        wheelbaseRatio: 0.55,                            // wheelbase relative to vehicle length; default 0.55
    },

    // Power (optional)
    power: {                                             // vehicle power settings
        maxSpeed: 100,                                   // base top speed in km/h, scaled by scale; default 100
        acceleration: 5,                                 // base acceleration in m/s², scaled by scale; default 5
        deceleration: 5,                                 // base braking deceleration in m/s², scaled by scale; default 5
    },
});

Runtime:

player.resetVehicle();                                  // right the currently driven vehicle and clear linear / angular velocity
player.setVehicleScale(vehicle, 0.12);                  // change the overall vehicle scale at runtime
player.setVehicleClearance(vehicle, 0.15);              // adjust chassis collision-box ground clearance
player.setVehicleChassisSizeScale(vehicle, {            // adjust chassis collision-box size multipliers relative to the automatic AABB
    x: 1,                                               // X-axis size multiplier
    y: 0.9,                                             // Y-axis size multiplier
    z: 1,                                               // Z-axis size multiplier
});
player.setVehiclePhysicsDebug(true);                    // show / hide the vehicle chassis physics collision box

Foot IK Plugin

import { FootIK } from "three-player-controller/foot-ik";

const footIK = new FootIK({
    // Manually specify the player skeleton mapping; common bone names are matched automatically when omitted
    skeleton: {                                         
        hips: "mixamorigHips",                          // hips / pelvis bone name

        // Left / right leg bone configuration
        legs: {                                         
            left: {                                     
                upper: "mixamorigLeftUpLeg",            // left upper-leg bone
                lower: "mixamorigLeftLeg",              // left lower-leg bone
                foot: "mixamorigLeftFoot",              // left foot bone
                toe: "mixamorigLeftToeBase",            // left toe bone
            },
            right: {                                   
                upper: "mixamorigRightUpLeg",           // right upper-leg bone
                lower: "mixamorigRightLeg",             // right lower-leg bone
                foot: "mixamorigRightFoot",             // right foot bone
                toe: "mixamorigRightToeBase",           // right toe bone
            },
        },
    },

    soleSkinThickness: 3,                               // skin-thickness compensation from the foot bone to the shoe sole
});

player.use(footIK);                                     // register the Foot IK plugin with the player controller

Runtime:

footIK.setEnabled(false);                 // enable / disable Foot IK
footIK.setDebugEnabled(true);             // show / hide Foot IK debug helpers
player.unuse(footIK);                     // unregister the Foot IK plugin from the player controller
footIK.dispose();                         // dispose debug objects and related resources created by Foot IK

player.destroy() automatically disposes registered plugins.

See the full example in example/footIK.js.

API

Lifecycle And Core

Method Description
init(opts, callback?) Initialize the controller.
update(dt?) Update the player, collisions, dynamic rigid bodies, vehicles, camera, and animation every frame.
destroy() Destroy the controller and release resources.
reset(pos?) Reset the player to the specified position or the initial position.
switchPlayerModel(options) Switch the player model at runtime using PlayerModelOptions.
use(plugin) Register a plugin.
unuse(plugin) Unregister a plugin.
getPlugins() Return a copy of the current plugin list.

Collision

Method Description
addCollider(desc) Add a static / kinematic / dynamic collider and return a ColliderHandle.
removeCollider(handle) Remove a collider by handle or id.
clearColliders(filter?) Clear colliders, optionally filtered by motion.
getColliderMeshes(options?) Return the Mesh colliders used by current player queries.
isStaticColliderUsable() Whether a static Mesh with a completed BVH is available.
getActiveKinematicCollider() Return the kinematic collider currently supporting the player.
getActiveDynamicBody() Return the dynamic rigid body currently supporting the player.
getDynamicBodies() Return all dynamic rigid bodies.
removeDynamicBody(body) Remove a dynamic rigid body.
clearDynamicBodies() Clear all dynamic rigid bodies.
raycastDynamicGround(...) Query the surface of dynamic rigid bodies below.

State

Method Return Value
getPosition() Current player position.
getVelocity() Current player velocity.
getIsFirstPerson() Whether first-person mode is active.
getIsFlying() Whether flight mode is active.
getIsOnGround() Whether the player is on the ground.
getGroundSupport() Current final ground-support point and world-space normal.
getCurrentDelta() Actual time step used by the current frame.
getControllerMode() 0 for player, 1 for vehicle.
getPlayerModel() Current player model.
getPlayerCapsule() Player capsule.
getActiveVehicle() Currently driven vehicle.
getAllVehicles() All loaded vehicles.
getCurrentPlayerAnimationName() Current animation clip name.
getCurrentLocomotionSet() Current Locomotion Set.
getCenterScreenRaycastHit() Center-screen raycast result.

Runtime Controls

Method Description
setInput(input) Inject external input.
setKeyMap(map?) Customize or restore default key bindings.
setMouseSensitivity(v) Set mouse sensitivity.
setPlayerScale(v) Change player scale at runtime.
setPlayerSpeed(v) Set walking speed.
setPlayerRunSpeed(v) Set running speed.
setPlayerFlySpeed(v) Set flight speed.
setJumpHeight(v) Set jump height.
setGravity(v) Set gravity.
setMinCamDistance(v) Set minimum third-person camera distance.
setMaxCamDistance(v) Set maximum third-person camera distance.
setCamLookAtHeightRatio(v) Set camera target height ratio.
setCamOverShoulderOffsetRatio(v) Set over-shoulder horizontal offset ratio.
setThirdMouseMode(v) Set third-person mouse mode.
setEnableZoom(v) Enable / disable mouse-wheel zoom.
setOverShoulderView(v) Enable / disable over-shoulder view.
setEnableToward(v) Enable / disable mouse-driven facing.
setSkipCapsuleCollision(v) Temporarily skip player capsule collision.
changeView() Switch first-person / third-person view.
setFirstPersonCamera(v?) Enter first-person view directly.

Debug

player.setColliderDebug(true);                       // show / hide static and kinematic Mesh collision wireframes
player.setPlayerCapsuleDebug(true);                  // show / hide the player capsule wireframe
player.setDynamicBodyDebug(true);                    // show / hide dynamic rigid-body collision wireframes
player.setVehiclePhysicsDebug(true);                 // show / hide the vehicle chassis physics collision box

Animation

Method Description
playPlayerAnimationByName(name, fade?) Play a player animation directly by clip name.
registerAnimation(key, clipName, opts?) Register a custom animation clip.
playAnimation(key, opts?) Play a registered custom animation.
registerLocomotionSet(setName, map) Register a locomotion animation set to replace built-in locomotion animations.
switchLocomotionSet(setName, fade?) Switch to the specified locomotion animation set.

registerAnimation

player.registerAnimation(key, clipName, {
    loop?: boolean,              // whether to loop; default true
    timeScale?: number,          // animation playback scale; default 1
    duration?: number,           // animation playback duration; default 0
    clampWhenFinished?: boolean, // whether to reset animation time to 0 when playback finishes; default false
    onFinished?: () => void,     // called when animation playback finishes
});

When both duration and timeScale are set, duration takes precedence.

playAnimation

player.playAnimation(key, {
    fade?: number,          // transition duration in seconds; default 0.18
    force?: boolean,        // when true, restart from the beginning even if this animation is already playing
    returnToPrev?: boolean, // applies only to LoopOnce animations; automatically restore the previous animation state after playback
});

registerLocomotionSet

Supported keys: idle | walking | walking_backward | running | jumping | flyidle | flying. Specified keys replace the corresponding built-in animations; omitted keys keep their existing animations.

player.registerLocomotionSet("combat", {
    idle: "CombatIdle",
    walking: "CombatWalk",
    walking_backward: "CombatBack",
    running: "CombatRun",
    jumping: "CombatJump",
    flyidle: "CombatFlyIdle",
    flying: "CombatFly",
});

Default Controls

Action Default Key Behavior
Forward W / ArrowUp Move forward
Backward S / ArrowDown Move backward
Left A / ArrowLeft Move left
Right D / ArrowRight Move right
Sprint Shift Player sprint; vehicle handbrake
Jump Space Jump; ascend while flying; vehicle brake
Toggle view V First-person / third-person
Fly F Toggle flight mode
Enter / exit vehicle E Vehicle interaction
Look Mouse Control view

Custom Key Bindings

Use keyMap to rebind any action in the table above to other keys, or disable an action. Key names use KeyboardEvent.code (for example "KeyE", "ArrowUp", and "Space"; note that it is "KeyE", not "e").

Each action supports three value forms:

  • Omitted → use the default key
  • String / string array → replace with the specified key(s); an array can bind multiple keys
  • null → disable the action; no key will trigger it

Configure during initialization:

await player.init({
    // ...
    keyMap: {
        forward: "KeyE",          // use E to move forward instead of default W / ↑
        jump: null,               // disable jumping
        left: ["KeyA", "KeyJ"],   // bind both A and J
        // omitted actions keep their default keys
    },
});

Switch key-binding schemes at runtime:

player.setKeyMap({ forward: "KeyI", backward: "KeyK" }); // apply new bindings
player.setKeyMap();                                      // restore all defaults

External Input

player.setInput({
    moveX: number,        // horizontal movement axis, range -1 to 1; positive moves right
    moveY: number,        // vertical movement axis, range -1 to 1; positive moves forward
    lookDeltaX: number,   // horizontal look delta, usually mousemove movementX
    lookDeltaY: number,   // vertical look delta, usually mousemove movementY
    jump: boolean,        // held state (true=pressed, false=released); jump, ascend while flying, brake while driving
    shift: boolean,       // held state (true=pressed, false=released); sprint, or handbrake while driving
    toggleView: boolean,  // trigger; pass true to switch first-person / third-person view
    toggleFly: boolean,   // trigger; pass true to toggle flight mode
    toggleVehicle: boolean, // trigger; pass true to enter / exit vehicle
});

Events

player.onAnimationChange = (name, action) => {};   // triggered when the current player animation changes
player.onBeforeViewChange = (isFirstPerson) => {}; // triggered before first-person / third-person switching
player.onViewChange = (isFirstPerson) => {};       // triggered after first-person / third-person switching
player.onGroundChange = (onGround) => {};          // triggered when grounded state changes
player.onVehicleEnter = (vehicle) => {};           // triggered after entering a vehicle
player.onVehicleExit = (vehicle) => {};            // triggered after exiting a vehicle
player.onTowardChange = (dx, dy, speed) => {};     // triggered when facing / view input updates

Main Options

PlayerControllerOptions

Field Type Required Default Description
scene THREE.Scene Yes — three.js scene instance.
camera THREE.PerspectiveCamera Yes — three.js camera instance.
controls any Yes — External camera controller, usually OrbitControls.
playerModelConfig PlayerModelOptions Yes — Player model and parameter configuration.
initPos THREE.Vector3 No (0, 0, 0) Initial spawn point.
colliders ColliderDesc[] No — Colliders registered in bulk during initialization.
mouseSensitivity number No 5 Mouse sensitivity.
minCamDistance number No 100 Minimum third-person camera distance.
maxCamDistance number No 440 Maximum third-person camera distance.
isShowMobileControls boolean No true Whether to show virtual control UI on mobile.
mobileControls MobileControlsOptions No All shown Mobile button visibility, icons, and layout settings.
thirdMouseMode 0 | 1 | 2 | 3 | 4 | 5 No 1 Mouse control mode in third-person view (0: hide cursor, control facing and view; 1: hide cursor, control view only; 2: show cursor, drag to control facing and view; 3: show cursor, drag to control view only; 4: show cursor, drag to control view while player facing follows the camera's horizontal direction; 5: hide cursor, control view while player facing follows the camera's horizontal direction).
enableZoom boolean No false Whether mouse-wheel zoom is enabled.
enableOverShoulderView boolean No false Whether over-shoulder view is enabled.
camOverShoulderOffsetRatio number No 0.2 Third-person over-shoulder camera horizontal offset ratio.
isFirstPerson boolean No false Whether to enter first-person immediately during initialization.
enableSpringCamera boolean No false Whether spring camera is enabled (the target follows the player with spring damping).
springCameraTime number No 0.05 Spring smoothing time in seconds; smaller values follow more tightly.
camLookAtHeightRatio number No 0.8 Third-person camera target height ratio (0=capsule bottom, 1=top).
timeScale number No 1 Time scale; <1 slow motion, >1 fast-forward.
keyMap KeyMap No Default bindings Custom key mapping; see Custom Key Bindings.

PlayerModelOptions

Field Type Required Default Description
model THREE.Object3D One of two — Root node of an already-loaded player model. Mutually exclusive with url.
animations THREE.AnimationClip[] With model — Player animation clips. Pass together with model.
url string One of two — Player model path (GLB/GLTF).
scale number Yes — Player model scale.
idleAnim string Yes — Idle animation name.
walkAnim string Yes — Walk animation name.
runAnim string Yes — Run animation name.
jumpAnim string | [string, string, string] Yes — Jump animation name. Pass a string for one animation, or a three-element [start, loop, land] array for three-stage playback; locomotion resumes automatically after landing.
leftWalkAnim string No walkAnim Left-strafe animation name; defaults to walkAnim when omitted.
rightWalkAnim string No walkAnim Right-strafe animation name; defaults to walkAnim when omitted.
backwardAnim string No walkAnim Backward animation name; defaults to walkAnim when omitted.
flyAnim string No idleAnim Flight animation name; defaults to idleAnim when omitted.
flyIdleAnim string No idleAnim Fly-idle animation name; defaults to idleAnim when omitted.
flyHoverForwardAnim string No flyAnim Hover animation while flying forward; defaults to flyAnim when omitted.
flyHoverBackAnim string No flyIdleAnim Hover animation while flying backward; defaults to flyIdleAnim when omitted.
flyHoverLeftAnim string No flyIdleAnim Hover animation while flying left; defaults to flyIdleAnim when omitted.
flyHoverRightAnim string No flyIdleAnim Hover animation while flying right; defaults to flyIdleAnim when omitted.
flyHoverUpAnim string No flyIdleAnim Hover animation while flying upward; defaults to flyIdleAnim when omitted.
flyHoverDownAnim string No flyIdleAnim Hover animation while flying downward; defaults to flyIdleAnim when omitted.
drivingAnim string No idleAnim Driving loop animation name; defaults to idleAnim when omitted.
gravity number No -2400 Base gravity value, scaled by scale.
jumpHeight number No 600 Base jump-height value, scaled by scale.
speed number No 200 Base movement speed, scaled by scale.
runSpeed number No 600 Base run speed, scaled by scale.
flySpeed number No 2100 Base flight speed, scaled by scale.
rotateY number No 0 Initial player facing direction in radians, used to change the model's initial facing direction.
headBoneName string No — Head bone or node name used for first-person camera attachment.
firstPersonCameraOffset [number, number, number] No Built-in default Local first-person camera offset; relative to the head bone when headBoneName is set, otherwise relative to the capsule.
capsuleRadiusRatio number No 1 Capsule radius multiplier for fine-tuning collision width.
acceleration number No 30 XZ acceleration response; larger values accelerate faster.
deceleration number No 30 XZ deceleration response; larger values stop faster.

MobileControlsOptions

Field Type Required Default Description
joystick boolean No true Whether to show a joystick with continuous 360° direction input.
jump boolean | JumpButtonOptions No true Jump / brake button; false hides it, or pass an object to customize image and layout.
fly boolean | MobileButtonOptions No true Flight button; false hides it, or pass an object to customize image and layout.
view boolean | MobileButtonOptions No true View-switch button; false hides it, or pass an object to customize image and layout.
vehicle boolean | MobileButtonOptions No true Enter / exit vehicle button; false hides it, or pass an object to customize image and layout.

MobileButtonOptions / JumpButtonOptions

Field Type Required Default Description
left / right number No Built-in position Distance from the left or right side of the screen in px. Setting left without right clears the default right positioning.
top / bottom number No Built-in position Distance from the top or bottom of the screen in px. Setting top without bottom clears the default bottom positioning.
size number No 56 Circular button diameter in px.
icon string No English label Custom image URL.
brakeIcon string No BRAKE label Used only by JumpButtonOptions; sets the brake image URL in vehicle mode.

ColliderDesc

Field Type Default Description
motion "static" | "kinematic" | "dynamic" — Collider motion type.
shape ColliderShape — Collision shape configuration; supports mesh, box, and sphere.
groups number Set automatically based on motion Collision group this collider belongs to.
mask number Set automatically based on motion Collision group mask this collider is allowed to collide with.
useWorker boolean false Whether to build the BVH asynchronously in a Worker for mesh + source.
userData unknown — Custom application data.
follow THREE.Object3D — Scene object followed by a kinematic collider.
density number 1 dynamic rigid-body density used to calculate mass.
restitution number 0.2 dynamic rigid-body restitution.
friction number 0.6 dynamic rigid-body friction.
linearDamping number 0.4 dynamic rigid-body linear damping.
angularDamping number 0.6 dynamic rigid-body angular damping.
gravity number Internal default dynamic rigid-body gravity.
velocity THREE.Vector3 (0,0,0) Initial linear velocity of a dynamic rigid body.
angularVelocity THREE.Vector3 (0,0,0) Initial angular velocity of a dynamic rigid body.
mesh THREE.Mesh — Visual Mesh required when creating a built-in dynamic box / sphere rigid body.
simulate boolean true Whether the built-in dynamic rigid-body system simulates this body.

ColliderShape

Mesh

Field Type Description
kind "mesh" Mesh collision shape.
source THREE.Object3D | THREE.Object3D[] Collect geometry from the object(s), merge the collision mesh, and build a BVH.
mesh THREE.Mesh Register an already-prepared collision Mesh directly.

Box

Field Type Description
kind "box" Box collision shape.
halfExtents THREE.Vector3 Box half-extents, i.e. half of the full size.
position THREE.Vector3 Initial world position, optional.
quaternion THREE.Quaternion Initial world rotation, optional.

Sphere

Field Type Description
kind "sphere" Sphere collision shape.
radius number Sphere radius.
position THREE.Vector3 Initial world position, optional.

VehicleOptions

Basic Parameters

Field Type Default Description
model THREE.Object3D — Root node of the externally loaded vehicle model.
position THREE.Vector3 — Initial vehicle world position.
wheelsNames [string, string, string, string] — Array of wheel node names in front-left, front-right, rear-left, rear-right order.
driverSeatPosition THREE.Vector3 — Driver-seat capsule center in vehicle chassis-local coordinates.
scale number 1 Vehicle model scale.
driverSeatRotation number 0 Driver-seat horizontal rotation relative to vehicle chassis local space in radians.
modelRotation number -Math.PI / 2 Initial vehicle model Y-axis rotation.
followVehicleDirection boolean true Whether the camera follows vehicle heading while driving.
debug VehicleDebugOptions — Vehicle physics debug visualization settings.
chassis VehicleChassisOptions — Chassis collision-box and damping settings.
suspension VehicleSuspensionOptions — Suspension and tire settings.
steering VehicleSteeringOptions — Steering settings.
grip VehicleGripOptions — Grip and handbrake settings.
power VehiclePowerOptions — Power settings.

debug

Field Type Default Description
showPhysicsBox boolean false Show the chassis physics collision box.
showWheelRays boolean false Show wheel suspension rays.
showWheelTravel boolean false Show wheel Y-axis travel range.
showWheelSpheres boolean false Show dynamic wheel collision spheres.

chassis

Field Type Default Description
density number 1 Chassis density; mass is calculated from collision-box volume and density.
linearDamping number 0.05 Chassis linear damping.
angularDamping number 0.5 Chassis angular damping.
clearance number Auto-calculated Height of the chassis box bottom relative to tire contact points.
sizeScale.x number 1 Chassis collision-box X-axis size multiplier.
sizeScale.y number 1 Chassis collision-box Y-axis size multiplier.
sizeScale.z number 1 Chassis collision-box Z-axis size multiplier.

suspension

Field Type Default Description
restLength number Auto-calculated Suspension rest length.
maxTravel number 0.35 Maximum suspension travel.
stiffness number 18 Suspension stiffness.
compression number 2.1 Suspension compression damping.
relaxation number 2.5 Suspension rebound damping.
maxForce number 6000 Maximum suspension force per wheel.
frictionSlip number 8 Longitudinal tire grip.
sideFrictionStiffness number 1 Lateral tire friction stiffness.
rollInfluence number 0.12 Effect of lateral force on body roll.

steering

Field Type Default Description
maxSteerAngle number Math.PI / 5 Maximum steering angle in radians.
steerTime number 0.45 Time from center to full steering lock in seconds.
steerReturnTimeSlow number 0.55 Low-speed steering return time in seconds.
steerReturnTimeFast number 0.4 High-speed steering return time in seconds.
highSpeedSteerScale number 0.3 Maximum steering ratio at top speed.

grip

Field Type Default Description
maxG number 1.2 Maximum lateral acceleration in g.
sideFrictionIdle number 1 Lateral friction while driving straight.
sideFrictionFrontMin number 0.55 Minimum front-wheel lateral friction under high load.
sideFrictionRearMin number 0.45 Minimum rear-wheel lateral friction under high load.
handbrakeRearFriction number 0.35 Rear-wheel lateral friction while handbraking.
handbrakeRearDriveScale number 0.65 Rear-wheel drive-force ratio while handbraking.
handbrakeReleaseTime number 0.15 Recovery time after releasing the handbrake in seconds.
wheelbaseRatio number 0.55 Wheelbase relative to vehicle length.

power

Field Type Default Description
maxSpeed number 100 Base top speed in km/h, scaled by scale.
acceleration number 5 Base acceleration in m/s², scaled by scale.
deceleration number 5 Base braking deceleration in m/s², scaled by scale.

Feedback

If you have any questions or suggestions, please submit an issue.

Acknowledgements