Inspiration
The sense of life in character animation often comes from what continues moving after the main action has stopped: a ribbon settling, hair lagging behind a turn, a skirt responding to gravity, or motion traveling through a rope or cape.
In 3ds Max, this kind of secondary motion is often created through extensive hand-keying or a collection of disconnected scripts. Hand-keying is slow to iterate, while fragmented tools can produce inconsistent motion and a gap between what artists preview and what eventually gets baked.
Ripple grew out of this recurring production problem. We wanted to turn bone-based secondary motion into a reliable and repeatable artist workflow: register the bones, preview and tune the motion, approve the result, bake keyframes, clean the scene, and hand engine-ready animation back to the normal production pipeline.
Our goal is not to build a render-grade cloth or hair simulator, nor to clone an existing tool. We built Ripple around one central promise: what artists preview is exactly what gets baked.
What it does
Ripple is a bone-physics plugin for animators working in 3ds Max 2024. It generates natural, controllable, and reproducible secondary motion for bone chains that have already been rigged and skinned.
The current v0.4 development build can:
- Register and validate bone chains, including multiple chains, branching hierarchies, and per-bone locking;
- Use the same in-house full-frame rigid-body core for both gravity-enabled and gravity-free motion;
- Provide non-destructive cached previews, frame-by-frame playback, A/B parameter comparison, and local recalculation;
- Bake the approved cache directly to bone keyframes, with Undo, write-back verification, and rollback on failure;
- Remove Ripple preview layers, caches, and helper data to restore a clean, bone-only animation scene;
- Guide registration, tuning, preview, baking, cleanup, progress tracking, and error recovery through a unified panel;
- Return the baked result to a standard FBX workflow validated for Unreal Engine 5 and Unity.
The honest boundary of the current build is that collision is not implemented yet. Sphere, capsule, and plane collision belong to the planned v0.5 milestone. Force fields and proxy-chain mapping are later roadmap features as well.
How we built it
Ripple uses a deliberately layered architecture:
- A PySide2 UI layer provides the 3ds Max panel;
- An application-service layer coordinates registration, caching, baking, and cleanup;
- A host-adapter layer isolates
pymxs, Custom Attributes, controllers, keyframes, and Undo; - A pure numerical layer implements the solver in Python, NumPy, and native extensions, allowing it to be tested outside 3ds Max.
The solver uses full-frame quaternion state and a fixed time step:
[ \Delta t = \frac{1}{fps \times substeps} ]
Topology order, constraint iterations, floating-point precision, and state-write order are fixed. Gravity is simply a force input inside the same physical world rather than a switch to another solver. Scene sampling and keyframe writes remain on the 3ds Max main thread, while pure numerical work runs on a worker thread without passing Max objects between threads.
Each solve also receives a SHA-256 fingerprint derived from the solver version, topology, parameters, drive track, frame rate, and scene units. Preview and bake consume the same cache. If an input or version changes, the cache fails closed instead of silently baking stale motion.
Ripple is delivered as a drag-installable .mzp package. Validation combines unit tests outside Max, in-Max integration tests, real-package testing, automated visual checks, and separate user sign-off for motion feel.
Challenges we ran into
The hardest problem was not making a chain move. It was making the motion feel natural while remaining deterministic, stable, and fast enough for production.
An early approach added an independent momentum layer on top of a first-order chase system. It passed some numerical checks, but real GUI evaluation revealed stiffness, double motion, and weak propagation along the chain. We rejected that architecture and rebuilt the system as genuinely coupled, second-order full-frame dynamics.
Another long-term risk came from using separate production paths for gravity-on and gravity-off simulation. That split would eventually fragment state ownership, cache behavior, and future collision semantics, so we converged both modes onto one in-house SI/TGS physics core.
A parameter-contract audit then uncovered failures that a polished demo could easily have hidden: twist recovery was not monotonic, and Swing/Twist limits could exceed their promised maximum angles. We preserved that result as a real NO-GO, kept the original thresholds, and fixed the solver instead. Performance qualification also went through several failed 85-bone runs before improving from 101.72 seconds to 30.21 seconds.
We also learned that headless tests can all pass while visible defects remain. Ripple therefore uses three distinct validation layers: numerical gates, reproducible visual evidence, and real user judgment of motion feel.
Accomplishments that we're proud of
What we are most proud of is not one isolated algorithm, but an honest and reproducible delivery loop.
The current ripple-FF-4.0.1 includes:
- One production state and constraint pipeline for gravity-on and gravity-off motion;
- Predictive joint-limit braking and an authoritative pose safety shell;
- Monotonic bounded twist recovery with independent damping;
- Deterministic cache checkpoints and exact continuation support;
- A complete path from preview and bake to a tested
.mzppackage.
The frozen qualification snapshot includes 1,436/1,436 repository tests, 34/34 parameter-contract checks, 54/54 objective checks in 3ds Max, performance evidence for 85- and 200-bone scenes, six formal visual subjects from a real package, and explicit user approval of seven motion-feel criteria.
Just as importantly, we preserved failure evidence and fail-closed compatibility boundaries instead of creating the appearance of success through silent migration, relaxed thresholds, or bulk re-signing of test results.
What we learned
First, a physics tool needs three independent layers of truth: numerical invariants, reproducible visual evidence, and real user judgment of motion feel. None can substitute for the others.
Second, determinism is not a property of one solver function. It is a system property created by input sampling, topology order, persistent state, cache fingerprints, version boundaries, and keyframe write-back.
Third, one authoritative state stack is safer than maintaining separate solvers for different modes. It reduces current semantic drift and lowers the long-term complexity of collision, caching, and checkpoint continuation.
Finally, preserving an honest failure is more valuable than keeping every report green. Metrics, thresholds, and test assets should be frozen before implementation changes begin, and mutation tests should prove that the gates genuinely turn red when protected behavior is broken.
What's next for Ripple
Ripple’s immediate next step is not collision. We will first complete the final v0.4 gate by recalibrating the built-in ribbon, hair, and skirt presets.
Before changing any values, we will freeze the current solver baseline and the G0–G4 measurement bands. The presets will then be tested across gravity modes, frame rates, substep counts, subtle motion, turns, sudden stops, reversals, joint limits, and recovery. Each preset must receive its own independent user sign-off in a real 3ds Max GUI session.
Milestone 3 can only be closed again after the complete objective matrix and all three sign-offs are finished. Only then will v0.5 begin, adding sphere, capsule, and plane collision to the same physics core. Later milestones will introduce wind and explosion fields, proxy-chain mapping, and the final product polish required for a v1.0 release.


Log in or sign up for Devpost to join the conversation.