Physics

@three-blocks/proPhysicsWebGPUWebGL
new Physics(config : Object)

Physics - Main Thread Physics Engine Interface

High-level API for initializing and controlling the physics engine from the main thread. Manages SharedArrayBuffer creation, worker lifecycle, and provides convenient accessors for physics state.

Architecture

The Physics acts as a facade between the main thread and the physics worker:

SharedArrayBuffer Communication

All physics state is synchronized via typed arrays backed by SharedArrayBuffer. Use the body reference from addBody() for state access:

// Store body reference from addBody()
const playerApi = await physics.addBody(BodyType.DYNAMIC, playerMesh);

// Sync physics to mesh with interpolation
playerApi.body.sync(alpha);

// Direct buffer access via body.view (advanced)
const view = playerApi.body.view;
console.log(view.positionx, view.positiony, view.positionz);

Basic Usage

import { Physics, PhysicsController, BodyType } from '@three-blocks/pro';
import * as THREE from 'three/webgpu';

const physics = new Physics({ gravity: -9.81, debug: false });
await physics.init();

// Add bodies - STORE the api reference
const playerApi = await physics.addBody(BodyType.DYNAMIC, playerMesh);
await physics.addBody(BodyType.STATIC, groundMesh);

// Start simulation
await physics.start();

// Game loop - use body.sync() for mesh synchronization
const controller = new PhysicsController(physics);
function animate() {
  controller.update(delta);
  playerApi.body.sync(controller.getInterpolationAlpha());
  renderer.render(scene, camera);
}

Events (Body-Level)

import { BodyEvent } from '@three-blocks/pro';

// Listen for collision events on a body
const api = await physics.addBody(BodyType.STATIC, wallMesh);
api.body.addEventListener(BodyEvent.COLLIDE, (event) => {
  console.log('Wall collision detected');
});

// Listen for zone triggers
const zone = await physics.addBody(BodyType.ZONE, triggerMesh);
zone.body.addEventListener(BodyEvent.ZONE_ENTER, (event) => {
  console.log('Player entered zone');
});
zone.body.addEventListener(BodyEvent.ZONE_LEAVE, (event) => {
  console.log('Player left zone');
});

// Listen for weapon hits (hitscan, projectile, or hybrid)
const npc = await physics.addBody(BodyType.KINEMATIC, npcMesh);
npc.body.addEventListener(BodyEvent.HIT, (event) => {
  console.log('NPC was hit!', event.hitPoint, event.shooterIndex);
});

Weapons System (Hitscan, Projectile, Hybrid)

import { WeaponType } from '@three-blocks/pro';

// Hitscan (instant raycast) - rifles, pistols, lasers
physics.weapons.fireFromCamera({
  type: WeaponType.HITSCAN,
  shooterIndex: 0,
  shooterType: 'dynamic',
  maxDistance: 100,
});

// Projectile (traveling with physics) - arrows, grenades, rockets
physics.weapons.fire(towerIndex, {
  type: WeaponType.PROJECTILE,
  bodyType: 'kinematic',
  direction: [0, 0, 1],
  speed: 40,
  gravity: 1,
});

// Hybrid (instant hit + visual projectile) - competitive FPS feel
physics.weapons.fireFromCamera({
  type: WeaponType.HYBRID,
  shooterIndex: 0,
  damage: 25,
});
Constructor Parameters
configoptionalObject
Configuration options.
Default is {}.
  • gravityoptionalnumber
    Gravity scalar.
    Default is -9.81.
  • linearDragoptionalnumber
    Linear drag coefficient.
    Default is 1.
  • maxDynamicBodiesoptionalnumber
    Maximum dynamic bodies.
    Default is 16.
  • maxKinematicBodiesoptionalnumber
    Maximum kinematic bodies.
    Default is 256.
  • maxStaticBodiesoptionalnumber
    Maximum static bodies.
    Default is 512.
  • maxZonesoptionalnumber
    Maximum zone triggers.
    Default is 128.
  • debugoptionalboolean
    Enable debug mode.
    Default is false.
  • physicsFPSoptionalnumber
    Physics update rate in FPS (can be overridden by physics model's targetFPS).
    Default is 60.
  • tickRateoptionalnumber
    Internal physics tick rate in Hz (e.g., 100 for low fidelity, 625 for high fidelity).
    Default is 625.
  • physicsModeloptionalstring
    Physics model preset ('arcade', 'platformer', 'realistic').
    Default is 'arcade'.
  • customPhysicsModeloptionalObject
    Custom physics model object implementing PhysicsModel interface.
    Default is null.
  • solverIterationsoptionalnumber
    Number of velocity solver iterations for D↔D collisions (more = stable stacks, slower).
    Default is 4.
  • positionIterationsoptionalnumber
    Number of position correction iterations for D↔D collisions.
    Default is 2.
See also
  • {@link physicsState} - Worker-side state container
  • {@link PhysicsController} - High-level player controller
Example
┌─────────────────────────────────────────────────────────────────┐
│                        MAIN THREAD                              │
│  ┌─────────────┐    ┌─────────────┐    ┌───────────────────┐   │
│  │ Your Game   │───▶│ Physics  │◀──▶│ SharedArrayBuffer │   │
│  │   Code      │    │  (facade)   │    │     Views         │   │
│  └─────────────┘    └──────┬──────┘    └───────────────────┘   │
│                            │ Comlink RPC                        │
└────────────────────────────┼────────────────────────────────────┘
                             ▼
┌─────────────────────────────────────────────────────────────────┐
│                      PHYSICS WORKER (60 FPS)                    │
│  - Collision detection (BVH raycasting)                        │
│  - Character controller (capsule-based)                        │
│  - Camera system (3rd person, 1st person, bird view)           │
│  - Trigger zones and events                                     │
└─────────────────────────────────────────────────────────────────┘

Properties

.weapons : PhysicsWeapons|null

Unified weapon system. Supports hitscan, projectile, and hybrid weapons with a single API.

.physicsState :

Get the global physics state view.

.cameraState :

Get the camera state view.

.gravity :

Get the current gravity scalar value. Negative values pull downward (e.g., -9.81 for Earth gravity).

.player :

Get the main player body buffer view (dynamic body at index 0).

Note: Prefer using the body reference from addBody() instead: playerApi.body.sync(alpha) for mesh sync, playerApi.body.view for buffer access.

.sharedKinematic :

Get the shared kinematic body views array. Used by KinematicAnimator to write animation values to SharedArrayBuffer.

.syncView :

Get the sync buffer view for Atomics-based synchronization.

.physicsApi :

Get the physics API for direct calls. Returns a proxied API that automatically registers static meshes for debug visualization.

.api :

Alias for physicsApi - provides cleaner access via physics.api

.workerApi :

Legacy alias for physicsApi - for backwards compatibility

.staticMeshes :

Get all static meshes array.

Methods

getInterpolationAlpha#

getInterpolationAlpha() : number

Get interpolation alpha, cached per frame. All consumers (cameras, body.sync, controllers) get identical value within same frame. This prevents timing mismatches when different components calculate alpha at different times.

Returns
number — Interpolation factor (0-1)

init#

init(workerInstance : Worker) : Promise<void>

Initialize the physics engine. Creates SharedArrayBuffers and initializes the worker.

Parameters
workerInstanceoptionalWorker
Optional custom worker instance
Default is null.
Returns
Promise<void>

start#

start() : Promise<string>

Start the physics simulation.

Returns
Promise<string> — Status message

stop#

stop() : Promise<string>

Stop the physics simulation.

Returns
Promise<string> — Status message

pause#

pause()

Pause the physics simulation.

resume#

resume()

Resume the physics simulation.

getView#

getView(bodyType : string) : Object

Get raw buffer view for a body type.

Internal: Prefer using the body reference from addBody() instead. Use api.body.view for direct buffer access, or api.body.sync(alpha) for mesh synchronization.

Parameters
bodyTypestring
Body type: 'dynamic', 'kinematic', 'static', 'zone', 'camera', 'physic'
Returns
Object — Buffer view

getDynamicBody#

getDynamicBody(index : number) : Object|null

Get a dynamic body buffer view by index.

Internal: Prefer using the body reference from addBody() instead. Store the api from addBody() and use api.body.sync(alpha) or api.body.view.

Parameters
indexnumber
The body index
Returns
Object | null — Body buffer view or null if not found

getKinematicBody#

getKinematicBody(index : number) : Object|null

Get a kinematic body buffer view by index.

Internal: Prefer using the body reference from addBody() instead. Store the api from addBody() and use api.body.sync(alpha) or api.body.view.

Parameters
indexnumber
The body index
Returns
Object | null — Body buffer view or null if not found

bindInput#

bindInput(inputController : InputController, options : Object)

Bind an InputController to automatically sync input to physics. Call this once after init() and after adding the player body.

This handles all internal view registration, so you don't need to manually call setPhysic() or access views directly.

Parameters
inputControllerInputController
The input controller instance
optionsoptionalObject
Binding options
Default is {}.
  • playerIndexoptionalnumber
    Which dynamic body receives input
    Default is 0.
  • jumpVelocityoptionalnumber
    Jump velocity to apply
    Default is 8.

updateInput#

updateInput()

Sync input state to physics each frame. Call this in your render loop when using bindInput().

addStaticBody#

addStaticBody(mesh : THREE.Object3D, options : Object) : Promise<number>

Add a static body and store mesh reference for debug visualization.

Parameters
meshTHREE.Object3D
The mesh to create a static body for
optionsObject
Static body options
Returns
Promise<number> — Static body index

addBody#

addBody(type : BodyType, meshOrCollideBox : THREE.Mesh|THREE.Group|THREE.InstancedMesh|THREE.BatchedMesh, properties : Object, bodyProps : Object, options : Object) : Promise<(Object|Array<Object>)>

Add a physics body from a mesh. Convenience method that wraps the addBody helper.

For BatchedMesh/InstancedMesh without instanceMatrixIndex, automatically batches ALL instances and returns an array of API objects.

Parameters
typeBodyType
Body type from BodyType enum: BodyType.STATIC, BodyType.DYNAMIC, BodyType.KINEMATIC, BodyType.ZONE
meshOrCollideBoxTHREE.Mesh | THREE.Group | THREE.InstancedMesh | THREE.BatchedMesh
The mesh, group, instanced mesh, or batched mesh to create physics for
propertiesoptionalObject
Physics properties
Default is {}.
  • frictionoptionalnumber
    Surface friction coefficient
  • massoptionalnumber
    Body mass (for dynamic bodies)
  • pFromoptionalnumber
    Platform start position (for moving platforms)
  • pTooptionalnumber
    Platform end position (for moving platforms)
bodyPropsoptionalObject
Body properties
Default is {}.
  • capsuleRadiusoptionalnumber
    Capsule collision radius (for dynamic/kinematic)
  • capsuleLengthoptionalnumber
    Capsule collision length (for dynamic/kinematic)
optionsoptionalObject
Optional settings
Default is {}.
  • hitboxoptionalTHREE.Mesh
    Custom hitbox mesh (overrides auto-generated collision)
  • debugMaterialoptionalTHREE.Material
    Material for debug visualization
  • sideoptionalnumber
    BVH side (e.g., THREE.DoubleSide)
  • instanceMatrixIndexoptionalnumber
    For InstancedMesh/BatchedMesh: which instance to use. If omitted, ALL instances are batched automatically.
  • sourceGeometryoptionalTHREE.BufferGeometry
    For BatchedMesh single instance: original geometry (skips extraction)
  • sourceGeometriesoptionalObject
    For BatchedMesh batch mode: map of geometryId -> BufferGeometry
  • customGeometryToBVHScriptoptionalfunction
    Custom geometry processing function
Returns
Promise<(Object|Array<Object>)> — The body API object(s). Returns array for batched BatchedMesh/InstancedMesh (when no instanceMatrixIndex provided).

  • index {number} - Body index in the physics system
  • type {string} - Body type ('static', 'dynamic', 'kinematic', 'zone')
  • entity {THREE.Object3D} - The mesh/entity passed in
  • collideBox {THREE.Mesh} - The collision mesh (may differ from entity)
  • body {Body|DynamicBody|KinematicBody} - Body instance with position/quaternion setters. For kinematic bodies, includes pathfinding methods (enablePathfinding, navigateTo, etc.)
  • instanceIndex {number} - (BatchedMesh/InstancedMesh only) The instance index within the mesh

Syncing to Three.js meshes: Use api.body.sync(alpha) to synchronize physics state to the mesh with interpolation. Store the api reference from addBody() and call sync() in your animation loop.

addBodies#

addBodies(type : BodyType, bodiesConfig : Array<Object>) : Promise<Array<Object>>

Add multiple physics bodies in a single batch operation. Significantly faster than calling addBody multiple times for many different meshes.

Note: For InstancedMesh/BatchedMesh where all instances share the same properties, use addBody() without instanceMatrixIndex instead - it auto-batches all instances. Use addBodies() when you have many different meshes to add.

Parameters
typeBodyType
Body type from BodyType enum: BodyType.STATIC, BodyType.DYNAMIC, BodyType.KINEMATIC, BodyType.ZONE
bodiesConfigArray<Object>
Array of body configurations
meshTHREE.Mesh | THREE.Group

See nested options below.

  • meshTHREE.Mesh | THREE.Group
    The mesh to create physics for
  • propertiesoptionalObject
    Physics properties
  • properties.frictionoptionalnumber
    Surface friction coefficient
  • properties.massoptionalnumber
    Body mass (for dynamic bodies)
  • bodyPropsoptionalObject
    Body properties
  • bodyProps.capsuleRadiusoptionalnumber
    Capsule collision radius
  • bodyProps.capsuleLengthoptionalnumber
    Capsule collision length
  • optionsoptionalObject
    Optional settings
  • options.hitboxoptionalTHREE.Mesh
    Custom hitbox mesh
  • options.sideoptionalnumber
    BVH side (e.g., THREE.DoubleSide)
  • options.customGeometryToBVHScriptoptionalfunction
    Custom geometry processing
Returns
Promise<Array<Object>>

Array of body API objects, each containing:

  • index {number} - Body index in the physics system
  • type {string} - Body type ('static', 'dynamic', 'kinematic', 'zone')
  • entity {THREE.Object3D} - The mesh/entity passed in
  • collideBox {THREE.Mesh} - The collision mesh
  • body {Body|DynamicBody|KinematicBody} - Body instance with position/quaternion setters

removeBody#

removeBody(bodyOrApiOrIndex : Body|DynamicBody|KinematicBody|Object|number, type : string) : Promise<string>

Remove a physics body from the simulation.

Accepts either a Body instance, an API object from addBody(), or type/index directly. Cleans up all main-thread references and notifies the worker to free the body slot.

Parameters

The body to remove:

  • Body instance (has type and index getters)
  • API object from addBody() (has type, index, body properties)
  • Body index (requires type parameter)
typeoptionalstring
Body type (required if first param is an index): 'static', 'dynamic', 'kinematic', 'zone'
Returns
Promise<string> — Confirmation message from worker

removeBodies#

removeBodies(bodiesOrApisOrIndices : Array<(Body|Object|number)>, type : string) : Promise<Array<string>>

Remove multiple physics bodies in a single batch operation.

More efficient than calling removeBody multiple times when removing many bodies. All bodies must be of the same type.

Parameters
bodiesOrApisOrIndicesArray<(Body|Object|number)>

Array of bodies to remove:

  • Body instances
  • API objects from addBody()
  • Body indices (requires type parameter)
typeoptionalstring
Body type (required if passing indices): 'static', 'dynamic', 'kinematic', 'zone'
Returns
Promise<Array<string>> — Array of confirmation messages from worker

createMeshPool#

createMeshPool(templateMesh : THREE.Mesh, poolSize : number, scene : THREE.Scene) : THREE.InstancedMesh

Create an InstancedMesh pool for projectile weapons with automatic mesh syncing.

Returns a THREE.InstancedMesh that automatically syncs with active projectile positions via Three.js onBeforeRender callback. Mesh instances are positioned, oriented, and interpolated smoothly between physics updates.

IMPORTANT: Only THREE.Mesh is supported. THREE.Group is NOT supported because InstancedMesh requires a single geometry and material. For complex projectile visuals, merge geometries into a single mesh or use the primary visual component.

Parameters
templateMeshTHREE.Mesh
Template mesh with geometry and material (NOT THREE.Group)
poolSizeoptionalnumber
Maximum simultaneous projectiles
Default is 10.
sceneoptionalTHREE.Scene
Scene to add mesh to (optional)
Returns
THREE.InstancedMesh — InstancedMesh with automatic syncing enabled

addWeapon#

addWeapon(type : string, mesh : THREE.Mesh|THREE.InstancedMesh|null, properties : Object) : Promise<Object>

Add a weapon instance to the physics system.

For projectile weapons with InstancedMesh (created via createMeshPool()), mesh instances automatically sync with active projectiles via Three.js onBeforeRender callbacks. No manual syncing required in your render loop.

Parameters
typestring
Weapon type ('hitscan', 'projectile', 'hybrid')
meshTHREE.Mesh | THREE.InstancedMesh | null
Visual mesh or InstancedMesh pool (use createMeshPool)
Default is null.
propertiesObject
Weapon configuration properties (damage, speed, gravity, etc.)
Returns
Promise<Object> — Weapon API object with { index, type, weapon, mesh }

removeWeapon#

removeWeapon(weaponIndexOrApi : number|Object)

Remove a weapon from the physics system.

Parameters
weaponIndexOrApinumber | Object
Weapon index or API object from addWeapon()

getStaticMesh#

getStaticMesh(index : number) : THREE.Object3D|null

Get the mesh associated with a static body index.

Parameters
indexnumber
Static body index
Returns
THREE.Object3D | null — The mesh or null

dispose#

dispose()

Dispose of the physics engine and free resources.

setFrustumConfig#

setFrustumConfig(config : Object) : Promise<string>

Configure the physics frustum culling system. Frustum culling optimizes collision detection by only testing bodies visible in the camera frustum. Near/far planes are inherited from the active camera automatically.

Parameters
configObject
Configuration options.
  • marginoptionalnumber
    Padding in world units around the frustum.
    Default is 10.
  • enabledoptionalboolean
    Enable/disable frustum culling.
    Default is true.
Returns
Promise<string> — Confirmation message from worker.

setSpatialGridConfig#

setSpatialGridConfig(config : Object) : Promise<string>

Configure the physics spatial grid optimization. Spatial grid provides finer-grained spatial filtering after frustum culling, reducing the number of bodies tested for collision based on proximity.

Parameters
configObject
Configuration options.
  • cellSizeoptionalnumber
    Size of each grid cell in world units.
    Default is 4.
  • enabledoptionalboolean
    Enable/disable spatial grid.
    Default is true.
Returns
Promise<string> — Confirmation message from worker.

Static Methods

isSupported#

isSupported() : boolean

Check if SharedArrayBuffer is supported.

Returns
boolean — True if supported