Ammo.js btDefaultMotionState and Custom Motion States

This article explains the role of btDefaultMotionState in Ammo.js, the JavaScript port of the Bullet physics engine. It covers how this default class handles the synchronization between physics simulations and 3D rendering engines, and provides clear guidelines on when and why you should write a custom motion state to optimize your WebGL projects.

What is the Purpose of btDefaultMotionState?

In Ammo.js, the physics simulation and the visual rendering engine (such as Three.js or Babylon.js) operate independently. While the physics world calculates collisions, gravity, and forces, the rendering engine needs to know where to draw the 3D meshes on the screen.

The primary purpose of a motion state is to act as a bridge between these two systems. btDefaultMotionState is the default implementation provided by Bullet/Ammo.js. Its responsibilities include:

While btDefaultMotionState successfully keeps track of a body’s physical position, it does not automatically update your visual 3D meshes. If you use the default motion state, you must manually query each rigid body’s transform in your main animation loop and copy those values over to your corresponding 3D meshes.

When Should You Write a Custom Motion State?

Writing a custom motion state is a common best practice in WebGL game development. You should write a custom motion state in the following scenarios:

1. To Automate Graphics Synchronization (Performance and Clean Code)

Instead of manually looping through every rigid body in your render loop to update visual positions, you can create a custom motion state. By overriding the setWorldTransform method, you can write code that directly updates your visual mesh’s position and rotation whenever the physics engine updates.

Because Bullet only calls setWorldTransform for active, moving bodies, this approach prevents unnecessary calculations for static or sleeping objects, saving CPU cycles.

2. To Implement Frame Interpolation

Physics engines usually step at a fixed rate (e.g., 60 ticks per second), whereas rendering engines run at the variable refresh rate of the monitor (e.g., 90Hz, 120Hz, or 144Hz). This mismatch can cause visual jitter.

A custom motion state allows you to implement interpolation. By storing the previous and current physics transforms, you can calculate an intermediate position based on the render frame’s remainder time, resulting in ultra-smooth visual movement.

3. To Trigger Game Logic on Movement

If you need to execute specific code when an object moves—such as updating a bounding box, triggering a sound effect, or emitting particles based on velocity—a custom motion state provides a clean hook to trigger these events directly when the physics engine alters the object’s transform.