Skip to content

Hair should sway, not turn into a tentacle ​

By Oleg Sidorkin, CTO and Co-Founder of Cinevva

Side-by-side comparison of the girl's layered hair with earlier motion on the left and improved motion on the right.

A character turns her head. Her hair should follow a moment later, swing a little, then settle. Our first version did something else: the ends curled underneath, as though the hairstyle were trying to become a tentacle.

The simulation stayed stable and preserved bone lengths, but those checks missed the accumulating curl.

This is the next piece of our auto-rigger: giving hair its own joints and secondary motion. We started with a simple test mesh, then moved to two real avatars. That changed both the implementation and what we measure.

First, rebuild a real ponytail ​

Seed-san has a separate ponytail mesh. For this test, we erased that mesh's original skin weights, attached it to the head, and asked our geometric fitter to build a new six-joint hair chain. The rest of the character kept its original skinning. The seven short bang chains also kept their artist-authored weights.

The fitter follows the connected hair surface outward from its root, places joints along that path, and blends nearby joint influences. A small band at the attachment stays pinned. This is a selected-clump workflow: we tell it which mesh is hair. It doesn't discover every strand in an arbitrary character.

Try the ponytail below. Use Hair motion to switch between the earlier and improved solver in a single preview. Choose Move, then settle to watch the hair come to rest. Pause to inspect a pose, or drag the preview to orbit. The Animation selector offers all 260 Universal animations. Choose a clip to see the whole body move, and Framing → Hair close-up brings the camera back to the strands. Head-turn test returns to the controlled comparison.

Open this demo in a full window ↗

We removed Seed-san's separate backpack and robot arm from these views so the ponytail is unobstructed. Hiding those accessories leaves the body, hair geometry, skin weights and animation unchanged. Backpack contact remains outside this comparison.

Try the Universal animation library ​

The animation selector uses the same world-space retargeter as Cinevva's rigger and runtime. We map each avatar's VRM humanoid metadata to the retargeter's expected joint names, select the body skin rather than an earlier hair-only skin, and capture the body's unanimated pose before the hair demonstration starts. This avoids treating a turned head or a crouch as the character's resting pose when another clip is selected.

A selected clip is fetched from the shared Universal library, retargeted once, and played on the selected character. Horizontal travel is removed to keep the character in view. Every selected Universal animation loops in these demos, including actions originally marked as one-shot. Restart replays the selected action. Hair motion runs after the body animation.

We retargeted all 260 clips on each avatar: 520 outputs, with no missing required body joints, invalid numeric tracks or body-animation tracks targeting the hair joints. We also sampled Idle, Walk, Jump, Back Flip, Dance, Crouch Forward and Sitting Idle, checked body motion and synchronization, and inspected rendered poses. The structural checks cover all clips on these two rigs. Rendered inspection covered the sampled actions. Retargeting check results.

Clothing needs its authored controls too. This VRM sample uses aim and twist constraints for its sleeves and limb deformation, so animating only humanoid joints leaves the sleeves in their original pose. The demo now loads these controls with the official VRM node-constraint module and updates them after the body animation, before hair simulation. The sleeves follow their supplied constraints, with no cloth simulation.

The full library is available for inspection. The clipping and stretch measurements below use the controlled head-turn sequence. Additional action checks are identified separately.

The mistake was in the resting shape ​

A chain of joints needs a direction to return toward. Our earlier solver calculated that direction relative to the already-bent joint above it. A small deflection at the top therefore changed the resting direction of the next segment, which changed the next one again.

That accumulation produced the curl. Keeping each bone the correct length could not prevent it.

The new solver remembers the hairstyle's original arc in the moving head's coordinate frame. Each segment still has inertia and damping, but its restoring direction comes from that original shape. The head can turn while the hair lags behind, and the lag no longer becomes a new resting curve for everything below it.

We also tuned the existing artist rigs a little firmer and increased damping. The comparison includes both that tuning and the coordinate-frame change.

A moving target that preserves the original arc ​

For segment i, let oi be its current joint position and ai its original child offset. The position it tries to return to is

ri=oi+RHRi0(sai).

RH is the animated rotation of the chain's attachment frame, usually the head. Ri0 is the accumulated original rotation from that frame to this segment, and s is model scale. The joint position follows the chain, but the target direction comes from the saved arc. Using the already-bent parent's rotation for that direction was what accumulated the curl.

Each step combines attraction toward this target with gravity and exponential damping:

ui=e−ch[vi+hfi],fi=k(ri−xi)+g,yi=xi+hui.

Here xi is the simulated endpoint, vi its velocity, k the restoring coefficient, c the damping rate, and g the gravity acceleration used by the solver. The demo samples body motion and hair together at h=1/120 second. This is a custom spring update with implicit unit mass rather than a full elastic-rod or XPBD solver.

We then put the endpoint back at its fixed distance Li=s‖ai‖ from the joint:

x^i=oi+Liyi−oi‖yi−oi‖.

This projection preserves bone length. It doesn't, by itself, preserve every triangle edge or keep the strand outside clothing. The implementation uses the resting direction if the candidate direction degenerates.

Then try a whole hairstyle ​

The second avatar, a pixiv sample, has twelve hair chains and 12,868 hair vertices. Here we preserve the artist's joints and weights and replace the secondary-motion behavior. This example uses the existing hair rig. Automatic reconstruction of a fused hair mesh remains future work.

Open this demo in a full window ↗

Look at the ends from the side and back. The improved version holds more of the intended silhouette while retaining movement. The earlier version gathers the ends into a more pronounced curl.

Real models also exposed a quieter bug: some short bangs end in a node that isn't itself a skinned bone. We were skipping those chains. The importer now accepts those endpoints and reads explicitly named head-hair chains from VRM 1 spring metadata. It also reads sphere and capsule collision proxies and per-joint collision radii.

VRM's spring-bone specification provides the interchange structure. Our motion is a custom solver with tuned defaults, rather than a complete implementation of every VRM spring setting. The demos use pixiv's MToon material loader for the authored shading, textures and face/clothing outlines. We disable the expanded hair outline shells because they intersect these thin layered surfaces. Both motion settings use identical lighting and materials. pixiv's three-vrm spring-bone implementation is an established reference for that ecosystem.

Why the front locks went through the shirt ​

Checking joint positions alone missed a visible failure: the strand between two joints could pass through a shoulder even when both ends were outside its collision shape. We also found an indexing mistake in our VRM radius handling: reading the next joint's radius gave the final segment a zero radius.

The two front locks now use collision checks along each entire segment. A shared fitting pass measures visible body and clothing geometry around the existing collision shapes, with expansion limits based on body size. Strand reach, position and skin-weight ownership identify which chains need segment checks. Both avatars now use this algorithm without model-name or strand-name rules. Expanding collision checks across the entire hairstyle increased stretching, so head attachment constraints and short bangs keep their original treatment.

We checked the result against the actual skinned shirt, independently of those proxies. Over one head-turn cycle, the shirt hid up to 555 of the 1,503 front-lock vertices with the earlier motion. The automatic fit recorded zero hits beyond the 1 mm measurement threshold, at both 30 and 120 frames per second. The earlier maximum measured depth was 75.2 mm. The measurement covers front-view occlusion during this head-turn cycle. Download the front-lock measurements.

Keeping the locks outside the shirt has a tradeoff: the layered hairstyle's 99th-percentile stretch increased from 17.1% before collision fitting to 18.0%. It remains below the earlier solver's 26.0%, and the table below reflects the current result.

Contact response needed a separate correction: pushing a strand out of clothing was being converted into velocity, causing a visible rebound on the next step. We now resolve overlap without adding that impulse, remove incoming motion at contact, and preserve sliding motion. A resting-strand regression checks both endpoint and full-segment contact: the corrected strand stays at the contact position instead of launching away.

Contact along the strand, without a rebound impulse ​

A hair link and a capsule axis are both line segments. Write their points as p(u) and q(v), with u,v∈[0,1]. Their clearance is

d=minu,v∈[0,1]‖p(u)−q(v)‖,C=d−(rb+rh).

rb is the body proxy radius and rh the hair collision radius, both in world units. Negative C means overlap. Checking only the child endpoint fixes u=1 and can miss the middle of a long lock. Segment contact searches the whole interval. A sphere is the special case where the capsule axis has zero length.

To avoid turning position correction into a bounce, we reconstruct velocity before pushing the endpoint out of contact:

vi∗=x^i−xih.

After a nonzero contact correction, let n be its normalized direction. We remove only the component moving into that correction:

vi+=vi∗−min(0,vi∗⋅n)n.

Tangential motion survives, so the strand can slide. The position correction itself isn't added to velocity. The solver repeats contact projection and removes velocity along the link's radial direction afterward. Interacting constraints can still leave residual overlap.

Back Flip exposed a different coverage gap on the rebuilt ponytail: its separate hair skin didn't contain the torso bones. Collider discovery now searches the body hierarchy, and the shared fitter adds a torso capsule where trunk coverage is missing. A six-second Back Flip check at both 30 and 120 frames per second found no sampled ponytail-link penetration into that capsule. The original head-turn deformation checks still pass.

Fast motion also needs checks between poses. The demo now advances the body, clothing constraints and hair together at 120 steps per second, and checks strands against moving sphere and capsule proxies between those steps. Contact keeps sideways sliding instead of stopping the entire strand, which otherwise made hair stick. Walk and Back Flip on both avatars produced identical sampled hair positions and velocities at 30, 60 and 120 frames per second in our four-second checks. These checks cover sampled sphere and capsule motion. Contact with individual clothing triangles and overlap introduced by later constraints remain unresolved. Download the frame-rate and contact checks.

That consistency has a cost. In the earlier two-view benchmark at 60 frames per second, the recorded 95th-percentile update time was about 1 ms for Seed-san and 14–23 ms for the layered model, excluding rendering. Those costs cover two characters, while the live demo now renders one. The layered case still needs optimization and measurement on phones.

The fitter still uses anatomical assumptions and bounded expansion. It preserves authored settings when the required body roles or geometry are missing, and reports unsupported transforms. Eight additional checks cover renamed bones, scale changes, clothing thickness, hidden accessories and restoring the original settings. These two avatars were used for tuning, so unseen hairstyles and garments still need testing before we'd call the result generally reliable.

Measure the mesh, not just the skeleton ​

A bone can keep its length while the triangles around it stretch. We therefore measured every hair triangle edge during a twelve-second run: four seconds of head movement, followed by eight seconds of settling. The simulation runs at 120 steps per second, and mesh measurements are sampled 30 times per second.

For each edge, we record its worst stretch during the run. The table reports the 99th percentile of those per-edge maxima: 99% of edges stayed at or below that amount of stretching.

For mesh edge e=(a,b) with original length ℓe0, we measure the skinned vertex positions za(t) and zb(t):

ρe(t)=‖za(t)−zb(t)‖ℓe0,me=maxt∈Tρe(t).

T contains the sampled frames. The reported percentage is 100(Q0.99({me})−1), where Q0.99 is the 99th percentile across edges. Taking each edge's maximum before the percentile retains brief stretching events that an average over time could hide. The worst-edge figure uses maxeme instead, so a small badly deformed region remains visible in the report.

Real-model testEarlier motionImproved motion
Seed-san: rebuilt ponytail plus original bangs8.0%5.0%
Seed-san: original hair rig7.6%5.1%
pixiv: original layered-hair rig26.0%18.0%

The worst individual edge on the layered model improved from 102.7% to 70.8% stretch, leaving substantial local distortion. Seed-san's worst edge improved from 35.2% to 27.8% in both cases. Both models were also used for tuning, and unseen hairstyles still need testing.

The chain roots stayed fixed. By the final two seconds, measured tip movement between sampled frames was below 0.001 mm in each improved case. Thirteen focused tests cover rigging, fixed roots, shape preservation, collisions, short endpoints, import and animation ownership. We also checked applying and undoing the rig in the viewer and exporting cyclic and one-shot animations.

Download the measurements. The live examples use the same solver code as the prototype's viewer and export path. The examples render interactively in the browser.

What this makes possible and what it doesn't yet solve ​

For a separate ponytail or lock, we now have a path from selected geometry to joints, weights and secondary motion. For an existing compatible hair rig, we can preserve the artist's work and animate it. When an animation already contains tracks for those hair joints, the procedural solver yields to the authored motion rather than applying a second movement on top.

The deformation benchmark covers head turns and gentle body translation. The separate Walk and Back Flip checks cover the specific contacts and frame rates described above. Broader motion quality still needs review. Collisions use sphere and capsule proxies, and hair self-collision is unimplemented. Braids, curls, fused hair regions and phone performance need their own tests.

The feature remains experimental. On these two characters, the revised solver and tuning reduced curling and improved the measured stretch.


Model credits. Seed-san © VirtualCast, Inc., from the official VRM sample collection. VRM1_Constraint_Twist_Sample © 2022 pixiv Inc., from three-vrm's example models. Both files include VRM Public License 1.0 metadata permitting redistribution and modification. The rebuilt ponytail and custom motion are our changes. Source URLs and file hashes identify the exact samples used.

Animation credits. The Universal clips are by Quaternius, from Universal Animation Library and Universal Animation Library 2, provided under CC0.

Next: reusable hair on a generated character ​

Our follow-up starts with unrigged library meshes: fit 46 hairstyles to a frog and inspect their extracted strand guides. The live experiment covers scalp attachment and an initial adapter for secondary motion.