How to Create Custom Ammo.js Constraints
Creating custom physics constraints in ammo.js by extending its base
classes directly in JavaScript is not natively supported due to the
WebAssembly boundary. Because ammo.js is a direct Emscripten port of the
C++ Bullet Physics engine, JavaScript cannot easily override C++ virtual
methods of base classes like btTypedConstraint. This
article explains the technical limitations of subclassing ammo.js base
classes in JavaScript and provides the practical workarounds used by
developers to achieve custom constraint behavior.
The WebAssembly Boundary Limitation
In standard object-oriented JavaScript, you can extend classes using
the extends keyword. However, ammo.js objects are wrappers
around compiled WebAssembly (or asm.js) memory pointers.
When the Bullet Physics engine updates the simulation, it calls
virtual C++ functions (such as getInfo1 and
getInfo2 inside btTypedConstraint) to
calculate constraint forces. Because the WebIDL binder used by
Emscripten does not automatically map these deep C++ virtual tables back
to arbitrary JavaScript functions, simply extending
Ammo.btTypedConstraint in JavaScript will result in the
engine ignoring your custom JS methods during the physics step.
Workaround 1: Using the Generic 6-DOF Constraint
Instead of writing a custom constraint from scratch, the most common
and robust approach is to configure the highly flexible
btGeneric6DofSpring2Constraint (or
btGeneric6DofConstraint).
This built-in constraint allows you to fully customize all six degrees of freedom (three translational axes and three rotational axes). You can create custom joints by: * Locking specific axes completely. * Leaving specific axes free to slide or rotate. * Setting custom upper and lower limits on motion. * Enabling springs, dampening, and custom motor forces on individual axes.
By tweaking these parameters at runtime in JavaScript, you can simulate almost any custom joint behavior without needing to inherit from base classes.
Workaround 2: Manual Force Application in Tick Callbacks
If your custom constraint requires complex logic that cannot be expressed via standard 6-DOF limiters, you can simulate the constraint manually using forces.
- Register a Pre-Tick or Post-Tick Callback: Use the physics world’s internal tick callback to run your custom JavaScript logic during every physics step.
- Calculate Custom Forces: In your callback, calculate the distance, velocity, or angle discrepancies between your two target rigid bodies.
- Apply Impulses: Use
applyCentralForce,applyTorque, orapplyImpulseon the rigid bodies to manually correct their positions and simulate your custom constraint.
While this approach is slightly less performance-efficient than native C++ constraints, it offers complete mathematical freedom directly inside JavaScript.
Workaround 3: Recompiling Ammo.js with Custom C++ Classes
If absolute performance is required and you must write a native C++
constraint, you can extend btTypedConstraint in the Bullet
C++ source code and recompile ammo.js.
- Add your custom C++ constraint class directly into the Bullet Physics source tree.
- Update the
ammo.idlfile to expose your new class and its custom methods to JavaScript. - Rebuild ammo.js using the Emscripten compiler.
Once compiled, your custom constraint class will be exposed on the
global Ammo object and can be instantiated in JavaScript
just like any other built-in constraint.