vidar.games

Active ragdoll

2026-11-04 · Discuss on X

Same 14 kg ball, same rapier.js world.
Left: the joint motors switch off at contact, so it's a plain ragdoll.
Right: 12 joint motors keep pulling toward the pose. It staggers and stands back up.

The problem

Ragdolls fall like puppets with cut strings, and keyframed hit reactions ignore what actually hit them. An active ragdoll sits in between: the body is fully simulated, but every joint has a motor that tries to hold a pose. Topic via @kethdev. I built one on rapier.js to see what it takes.

How it works

  1. Every limb is a rigid body: 13 bodies, 86.7 kg in total. No animation clip or IK moves them. The physics engine does.
  2. Every joint is a revolute ImpulseJoint with a position motor, configureMotorPosition(target, stiffness, damping). With MotorModel.ForceBased, stiffness is torque per radian of error. So the motor is a spring-damper the solver applies inside the joint constraint.
  3. Targets are a standing pose with slightly bent knees. Hips, knees and ankles get 1,500 N·m/rad. The spine gets 0.7× that. Arms get 0.04× or less, so they flop.
  4. A balance rule runs every step. It measures how far the centre of mass is from the feet, and how fast it's moving. Then it adds that offset to the ankle and hip targets, so the body leans back over its feet.

What I hit

Pose tracking alone doesn't balance anything. After a hit, the motors hold the shape and the whole body tips over its heel like a statue. The heel is only 10 cm behind the ankle.

The balance rule fixed small hits. In early tests it survived an 8 kg ball and fell at 9 kg. So I added a third piece that many active-ragdoll setups use: a capped upright torque on the pelvis. It's 300 N·m/rad, capped at 112.5 N·m. It isn't a joint muscle. It's a helper force, and the video labels it.

With MotorModel.AccelerationBased, the same numbers couldn't hold the body up. ForceBased did.

My build

rapier.js 0.19.3 (the newest release at commit 9f638c5), stepped headless at 240 Hz with 8 solver iterations, then played back in three.js. The mannequin, pendulum and balance rule are mine. The ball hits the chest at 5.0 m/s. The active body leans back 27.5° and is upright again 2.8 s after the hit, without moving its feet.

The stiffness sweep: at 400 it can't hold the pose and falls. At 10,000 it stays rigid and skids 0.9 m across the floor.

Trade-offs

When to use it

Melee and physics games where hits should look different every time, and the character has to stay on its feet without an animator authoring each reaction.

New breakdowns go out on X first: @vidar_0208. If this one helped, you can buy me a coffee.

Source and credits

Topic credit: @kethdev, active ragdoll (post: https://x.com/kethdev/status/2116661180102226155)

Physics: rapier.js by Dimforge, https://github.com/dimforge/rapier.js (Apache-2.0 license), source read at commit 9f638c5 (newest CHANGELOG entry 0.19.3); run as the npm package @dimforge/rapier3d-compat 0.19.3. Read for the API: src.ts/dynamics/impulse_joint.ts (RevoluteImpulseJoint, configureMotorPosition, configureMotorModel / MotorModel.ForceBased).
Rendering: three.js (MIT license), https://github.com/mrdoob/three.js, npm 0.170.

Code: the rapier.js library is used unchanged through its public API; no source files were copied or modified. The mannequin, joint layout, motor values, balance rule, pelvis upright torque, pendulum rig, playback renderer and compositing are my own. Everything in the video was rendered by me; no footage or screenshots from the original post. No external models or textures (stone and floor textures are generated noise). No IA model in this video.