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.

  1. 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.
  2. Calculate Custom Forces: In your callback, calculate the distance, velocity, or angle discrepancies between your two target rigid bodies.
  3. Apply Impulses: Use applyCentralForce, applyTorque, or applyImpulse on 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.

  1. Add your custom C++ constraint class directly into the Bullet Physics source tree.
  2. Update the ammo.idl file to expose your new class and its custom methods to JavaScript.
  3. 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.