Read Stress and Tension in Ammo.js Soft Bodies

This article explains how to measure and retrieve localized tension and stress values across an Ammo.js soft body. By analyzing the deformation of the underlying spring-like links that connect the soft body nodes, you can calculate real-time structural strain for visualization, gameplay mechanics, or destruction systems.

In Ammo.js (the Emscripten port of the Bullet Physics engine), soft bodies are represented by btSoftBody, which consists of nodes (vertices) and links (edges/springs connecting the nodes). Ammo.js does not provide a direct, pre-calculated “stress” variable for each vertex. Instead, localized tension must be calculated manually by measuring the deformation of individual links relative to their original rest length.

To read these tension values, you must iterate through the soft body’s links array. Each link connects two nodes and stores its original structural “rest length.” By comparing the current distance between the two connected nodes against the rest length, you can determine the strain (tension or compression) at that specific location.

Here is the step-by-step process and JavaScript code to calculate these values using the Ammo.js API:

// Assuming 'softBody' is an instance of Ammo.btSoftBody
const links = softBody.m_links;
const linkCount = links.size();

for (let i = 0; i < linkCount; i++) {
    const link = links.at(i);
    
    // Retrieve the two nodes connected by this link
    const nodeA = link.get_m_n(0);
    const nodeB = link.get_m_n(1);
    
    // Get current 3D positions of both nodes
    const posA = nodeA.m_x;
    const posB = nodeB.m_x;
    
    // Calculate the current distance between the nodes
    const dx = posA.x() - posB.x();
    const dy = posA.y() - posB.y();
    const dz = posA.z() - posB.z();
    const currentLength = Math.sqrt(dx * dx + dy * dy + dz * dz);
    
    // Retrieve the rest length of the link
    const restLength = link.m_rl;
    
    // Calculate tension (strain). 
    // Positive values indicate tension (stretching), negative indicates compression.
    const tension = (currentLength - restLength) / restLength;
    
    // 'tension' can now be mapped to vertex colors or used for structural damage logic
}

If you need to visualize this stress across a 3D mesh (such as a Three.js buffer geometry), you should map the calculated link tension back to the corresponding vertices. Because multiple links share the same node, you can average the tension values of all connected links at each node. This average value can then be written directly to a custom shader vertex attribute, allowing you to dynamically color the soft body—shifting from blue (low stress) to red (high stress) in real time.