How to Use Ammo.js Internal Tick Callbacks
This article explains how to set up and use custom JavaScript callback functions within the ammo.js physics engine during its internal simulation ticks. You will learn why these callbacks are necessary for precise physics manipulation, how to register them using the dynamics world API, and how to differentiate between pre-tick and post-tick execution.
Yes, you can set up custom JavaScript callback functions that fire during the internal ticks of ammo.js. Because ammo.js is a direct port of the Bullet Physics library, it inherits the capability to execute user-defined code at each internal physics substep rather than just once per render frame.
Why Internal Tick Callbacks are Necessary
In a typical web application, the physics world is updated once per
render frame using dynamicsWorld.stepSimulation(timeStep).
However, to maintain simulation accuracy, ammo.js may split a single
frame’s time step into multiple internal substeps (ticks).
If you apply forces or modify velocities in your main render loop, those changes only occur once per frame. To apply continuous forces (like gravity zones or wind) or to read collision data precisely at every physics calculation, you must hook into the internal substeps using tick callbacks.
The
setInternalTickCallback Method
To register a callback, you use the
setInternalTickCallback method available on the
btDynamicsWorld object.
Syntax
dynamicsWorld.setInternalTickCallback(callbackPointer, userPersistentData, isPreTick);callbackPointer: A pointer to your JavaScript function. Because ammo.js is compiled via Emscripten, you must wrap your JavaScript function usingAmmo.addFunctionto create a C++ compatible function pointer.userPersistentData: An optional argument (usually set tonullor0) to pass custom user data to the callback.isPreTick: A boolean flag. Set this totrueto run the callback before the physics substep simulation. Set it tofalse(or omit it) to run the callback after the physics substep simulation.
Step-by-Step Implementation
Below is a practical example of how to implement and register a post-tick callback in JavaScript.
1. Create the Callback Function
The callback function receives two arguments from the physics engine: a pointer to the dynamics world and the duration of the current physics time step.
function myTickCallback(worldPtr, timeStep) {
// Wrap the raw pointer back into an Ammo object to use it
const world = Ammo.wrapPointer(worldPtr, Ammo.btDynamicsWorld);
// Your custom physics logic goes here
// For example, applying a constant upward force to a specific body
}2. Wrap the Function with Emscripten
Because ammo.js runs in WebAssembly/asm.js, you must register the
JavaScript function with Emscripten’s function table. The signature
string 'vif' corresponds to: * v:
void (return type) * i: int /
pointer (first argument: worldPtr) * f:
float (second argument: timeStep)
const callbackPointer = Ammo.addFunction(myTickCallback, 'vif');3. Register the Callback with the Dynamics World
Pass the wrapped function pointer to your dynamics world.
// Register as a post-tick callback (runs after each physics substep)
dynamicsWorld.setInternalTickCallback(callbackPointer, 0, false);If you need the logic to run before the physics engine calculates
collisions and forces for that substep, set the third parameter to
true:
// Register as a pre-tick callback (runs before each physics substep)
dynamicsWorld.setInternalTickCallback(callbackPointer, 0, true);Cleaning Up
To prevent memory leaks when destroying your physics world or changing scenes, you should free the allocated Emscripten function pointer:
// Remove the callback from the world
dynamicsWorld.setInternalTickCallback(null);
// Free the function pointer from Emscripten memory
Ammo.removeFunction(callbackPointer);