Implementing Springs with Ammo.js Constraints

This article explains how to implement artificial spring behavior in physics simulations using native Ammo.js constraints. You will learn which constraints support spring dynamics, how to configure their stiffness and damping properties, and see a practical code implementation to create realistic elastic connections between rigid bodies.

To create spring behavior in Ammo.js (the WebAssembly/JavaScript port of the Bullet Physics engine), you must use the btGeneric6DofSpringConstraint. This powerful constraint restricts motion along six degrees of freedom (three translational axes and three rotational axes) and allows you to turn any of these axes into an active spring.

The btGeneric6DofSpringConstraint Class

The btGeneric6DofSpringConstraint maps its degrees of freedom to indices from 0 to 5: * 0, 1, 2: Translational axes (X, Y, Z) * 3, 4, 5: Rotational axes (X, Y, Z)

To turn a specific axis into a spring, you must enable the spring for that axis index, set its stiffness, and apply a damping factor to prevent endless oscillation.

Step-by-Step Implementation

1. Define the Transforms and Create the Constraint

First, define the attachment points (frames of reference) for both rigid bodies. If you want to connect a rigid body to a static point in space, you can pass an empty rigid body or a static body as the second participant.

// Define local attachment points for Body A and Body B
const transformA = new Ammo.btTransform();
transformA.setIdentity();
transformA.setOrigin(new Ammo.btVector3(0, 0, 0)); // Center of Body A

const transformB = new Ammo.btTransform();
transformB.setIdentity();
transformB.setOrigin(new Ammo.btVector3(0, 2, 0)); // Offset 2 units above Body B

// Create the 6-DoF Spring Constraint
const useLinearReferenceFrameA = true;
const spring = new Ammo.btGeneric6DofSpringConstraint(
    bodyA, 
    bodyB, 
    transformA, 
    transformB, 
    useLinearReferenceFrameA
);

2. Configure Spring Limits

Before configuring the spring behavior, define the motion limits for the axes. If an axis is completely locked or free, the spring will behave differently.

// Allow translation along the Y-axis (index 1) between -5 and 5 units
spring.setLinearLowerLimit(new Ammo.btVector3(-10, -5, -10));
spring.setLinearUpperLimit(new Ammo.btVector3(10, 5, 10));

3. Enable and Tune the Spring

Enable the spring behavior on your target axis (in this case, the Y-axis, which is index 1).

const yAxis = 1;

// Enable spring behavior on the Y-axis
spring.enableSpring(yAxis, true);

// Set Stiffness: Higher values make the spring tighter and faster
spring.setStiffness(yAxis, 35.0);

// Set Damping: Controls energy loss. 
// A value of 0.0 is no damping (infinite bounce), 1.0 is critically damped
spring.setDamping(yAxis, 0.5);

4. Set the Equilibrium Point

The equilibrium point is the resting position of the spring. You can set it to the current constraint configuration using setEquilibriumPoint().

// Sets the current position of the constraint as the default rest length
spring.setEquilibriumPoint(); 

5. Add the Constraint to the Physics World

Finally, add the newly configured spring constraint to your Ammo.js physics world.

// The second parameter disables collisions between the two linked bodies
physicsWorld.addConstraint(spring, true);

Key Configuration Tuning