# How to Implement Ragdoll Physics with Soft Keying in Jolt Physics

> Implement ragdoll physics with soft keying in Jolt Physics. Keep characters dynamic and responsive to collisions while following animations. Learn to drive bodies toward animation poses using velocities.

- Repository: [Jorrit Rouwe/JoltPhysics](https://github.com/jrouwe/JoltPhysics)
- Tags: how-to-guide
- Published: 2026-07-17

---

**Jolt Physics supports soft-keyframed ragdolls by keeping bodies dynamic while driving them toward animation poses using velocities, allowing characters to react to collisions while following keyframes.**

Implementing **ragdoll physics with soft keying in Jolt Physics** requires balancing animation fidelity with physical reactivity. Unlike pure kinematic rigs that ignore collisions, soft-keyframed ragdolls remain dynamic bodies that external forces can disrupt, making them ideal for interactive characters that need to collide with the environment while maintaining locomotion.

## Loading and Configuring Ragdoll Settings

Start by loading a ragdoll definition from an ObjectStream file or generating one programmatically. The `RagdollLoader` utility in the Jolt Physics samples provides a convenient entry point.

### Loading from ObjectStream Files

Use `RagdollLoader::sLoad()` to deserialize a ragdoll definition from a `.tof` file. This returns a `RagdollSettings` object that configures the skeletal hierarchy and joint constraints.

```cpp
#include <Samples/Utils/RagdollLoader.h>

// Load ragdoll settings with dynamic motion type
Ref<RagdollSettings> ragdollSettings = RagdollLoader::sLoad(
    "Human.tof", 
    EMotionType::Dynamic
);

```

*Source:* [`Samples/Utils/RagdollLoader.h`](https://github.com/jrouwe/JoltPhysics/blob/main/Samples/Utils/RagdollLoader.h) lines 30-32.

### Stabilizing the Rig

Before instantiation, preprocess the settings to improve solver stability. Call `Stabilize()` to adjust masses, `CalculateConstraintPriorities()` to optimize joint solving order, and `DisableParentChildCollisions()` to prevent adjacent body parts from colliding.

```cpp
// Optimize constraint solving
ragdollSettings->Stabilize();
ragdollSettings->CalculateConstraintPriorities();
ragdollSettings->DisableParentChildCollisions();

```

## Limiting Velocities for Stability

Soft-keyframed rigs risk simulation instability when animation data instructs rapid movement—such as a head snapping against a wall. Cap the linear velocity on each body part to prevent the solver from generating excessive impulses.

```cpp
// Limit max linear velocity to prevent jitter
for (BodyCreationSettings &bcs : ragdollSettings->mParts)
{
    bcs.mMaxLinearVelocity = 10.0f;
}

```

*Reference:* [`Samples/Tests/Rig/SoftKeyframedRigTest.cpp`](https://github.com/jrouwe/JoltPhysics/blob/main/Samples/Tests/Rig/SoftKeyframedRigTest.cpp) lines 51-53.

## Creating the Runtime Ragdoll

Instantiate the runtime `Ragdoll` object from your settings and add it to the physics system. The `CreateRagdoll()` method takes an ID, a collision group, and the target physics system.

```cpp
// Create runtime ragdoll instance
Ref<Ragdoll> ragdoll = ragdollSettings->CreateRagdoll(
    0, 
    0, 
    physicsSystem
);

// Add to simulation and activate
ragdoll->AddToPhysicsSystem(EActivation::Activate);

```

*Reference:* [`Samples/Tests/Rig/SoftKeyframedRigTest.cpp`](https://github.com/jrouwe/JoltPhysics/blob/main/Samples/Tests/Rig/SoftKeyframedRigTest.cpp) lines 55-57.

## Sampling Animation and Driving the Pose

The soft-keyframed technique works by sampling target poses from animation data and computing velocities that push dynamic bodies toward those targets.

### Loading Animation Data

Load a skeletal animation file and initialize a `SkeletonPose` that matches the ragdoll's skeleton structure.

```cpp
// Load animation (e.g., walk cycle)
Ref<SkeletalAnimation> walkAnim;
AssetStream stream("Human/walk.tof", std::ios::in);
ObjectStreamIn::sReadObject(stream.Get(), walkAnim);

// Initialize pose with ragdoll skeleton
SkeletonPose pose;
pose.SetSkeleton(ragdollSettings->GetSkeleton());

```

*Reference:* [`Samples/Tests/Rig/SoftKeyframedRigTest.cpp`](https://github.com/jrouwe/JoltPhysics/blob/main/Samples/Tests/Rig/SoftKeyframedRigTest.cpp) lines 58-66.

### Applying Kinematic Driving

Each frame, sample the animation at the current time, calculate joint matrices, then call `DriveToPoseUsingKinematics()` to compute the necessary linear and angular velocities. Counteract gravity unless you want the animation to include free-fall.

```cpp
void PrePhysicsUpdate(const PreUpdateParams &inParams)
{
    // Sample target pose from animation
    walkAnim->Sample(currentTime, pose);
    pose.CalculateJointMatrices();
    
    // Drive ragdoll toward target pose using velocities
    ragdoll->DriveToPoseUsingKinematics(pose, inParams.mDeltaTime);
    
    // Cancel gravity to maintain animation trajectory
    ragdoll->AddLinearVelocity(
        physicsSystem->GetGravity() * inParams.mDeltaTime
    );
    
    currentTime += inParams.mDeltaTime;
}

```

*Reference:* [`Samples/Tests/Rig/SoftKeyframedRigTest.cpp`](https://github.com/jrouwe/JoltPhysics/blob/main/Samples/Tests/Rig/SoftKeyframedRigTest.cpp) lines 84-92.

## Complete Implementation Example

Combine all steps into a complete setup function and update loop:

```cpp
#include <Jolt/Physics/Ragdoll/Ragdoll.h>
#include <Samples/Utils/RagdollLoader.h>

class SoftKeyframedCharacter
{
    Ref<RagdollSettings> mSettings;
    Ref<Ragdoll> mRagdoll;
    Ref<SkeletalAnimation> mAnimation;
    SkeletonPose mPose;
    float mTime = 0.0f;
    
public:
    void Initialize(PhysicsSystem *inPhysicsSystem)
    {
        // 1. Load settings
        mSettings = RagdollLoader::sLoad("Human.tof", EMotionType::Dynamic);
        
        // 2. Optimize and stabilize
        mSettings->Stabilize();
        mSettings->CalculateConstraintPriorities();
        mSettings->DisableParentChildCollisions();
        
        // 3. Limit velocities for soft-keyframing
        for (BodyCreationSettings &bcs : mSettings->mParts)
            bcs.mMaxLinearVelocity = 10.0f;
        
        // 4. Create runtime instance
        mRagdoll = mSettings->CreateRagdoll(0, 0, inPhysicsSystem);
        mRagdoll->AddToPhysicsSystem(EActivation::Activate);
        
        // 5. Load animation
        AssetStream stream("Human/walk.tof", std::ios::in);
        ObjectStreamIn::sReadObject(stream.Get(), mAnimation);
        mPose.SetSkeleton(mSettings->GetSkeleton());
    }
    
    void Update(float inDeltaTime, PhysicsSystem *inPhysicsSystem)
    {
        // Sample animation
        mAnimation->Sample(mTime, mPose);
        mPose.CalculateJointMatrices();
        
        // Drive physics bodies toward animation pose
        mRagdoll->DriveToPoseUsingKinematics(mPose, inDeltaTime);
        
        // Maintain animation trajectory against gravity
        mRagdoll->AddLinearVelocity(inPhysicsSystem->GetGravity() * inDeltaTime);
        
        mTime += inDeltaTime;
    }
};

```

## Summary

- **Load ragdoll definitions** using `RagdollLoader::sLoad()` from `.tof` files or create them programmatically.
- **Stabilize settings** with `Stabilize()` and `CalculateConstraintPriorities()` before instantiation.
- **Limit `mMaxLinearVelocity`** on each body part to prevent instability when animations demand rapid movement.
- **Create runtime ragdolls** via `RagdollSettings::CreateRagdoll()` and add to the physics system with `AddToPhysicsSystem()`.
- **Drive poses** using `DriveToPoseUsingKinematics()` while counteracting gravity to maintain animation fidelity.
- **Reference implementation** resides in [`Samples/Tests/Rig/SoftKeyframedRigTest.cpp`](https://github.com/jrouwe/JoltPhysics/blob/main/Samples/Tests/Rig/SoftKeyframedRigTest.cpp) according to the Jolt Physics source code.

## Frequently Asked Questions

### What is the difference between soft-keyframed and kinematic ragdolls?

**Soft-keyframed ragdolls remain dynamic bodies** that calculate velocities to reach target poses, allowing them to react to collisions and external forces. Kinematic ragdolls directly teleport bodies to positions, ignoring the physics simulation entirely. Use soft-keyframing when you need characters to collide realistically with the environment while following animation.

### Why must I limit `mMaxLinearVelocity` in soft-keyframed setups?

Without velocity caps, the physics solver generates excessively large impulses when animations instantaneously move body parts into impossible positions—such as a head intersecting a wall. Limiting `mMaxLinearVelocity` to values like `10.0f` keeps the simulation stable by capping the maximum correction speed, as implemented in [`SoftKeyframedRigTest.cpp`](https://github.com/jrouwe/JoltPhysics/blob/main/SoftKeyframedRigTest.cpp).

### How do I prevent gravity from pulling down a soft-keyframed ragdoll?

Call `ragdoll->AddLinearVelocity(physicsSystem->GetGravity() * deltaTime)` after driving the pose. This applies an opposite velocity that exactly counters gravitational acceleration for that frame, allowing the ragdoll to follow the animation trajectory without falling. Remove this compensation if you want the ragdoll to react to gravity while still being driven by animation.

### Can I use procedural animation instead of pre-baked animations?

Yes. The `SkeletonPose` structure accepts any transform data, whether sampled from `SkeletalAnimation` or generated procedurally. Calculate your procedural joint matrices, populate the `SkeletonPose`, and pass it to `DriveToPoseUsingKinematics()` exactly as you would with loaded animation data. The soft-keyframed system treats all pose sources identically.