vidar.games

Strand hair simulation

2026-11-11 ยท Discuss on X

6,000 strands on a turning head. On the left only gravity and length hold them, and the hair drops straight through the skull. On the right, shape constraints and two spheres keep the style.

The problem

A strand as a chain of particles is easy to keep at the right length. Keeping a hairstyle is the hard part. With only gravity and length constraints every strand hangs straight down from its root, through the head and the shoulders. After two seconds two thirds of the free particles were inside the head or shoulders.

How it works

Each strand is 16 particles with the root fixed to the scalp. One step runs five passes in order.
Integrate: every free particle moves by gravity times the timestep squared.
Global shape: the first four free particles are pulled toward where the groomed strand sits on the head right now. This sets the direction the hair leaves the scalp.
Local shape: walking down the strand, each segment is compared with the groomed segment, rotated into the frame of the segment before it. Both particles are nudged toward that direction. Curls survive without being pinned in space.
Sphere collision: a particle inside a sphere is moved to its surface.
Length: from root to tip, each particle is pulled back toward its rest distance from the one before.

What I hit

The repo's integration pass is named Verlet, but its velocity term cancels out. The new position is the old one plus gravity, so strands have no momentum. They follow the head, sag and settle, and never swing past. I kept it as written and labelled the left side by what it does.
The length table is filled by a loop that stops one particle early, so each tip gets a rest length of zero and folds onto its neighbour. I filled in the last entry.
The length pass is a soft pull. Under gravity the segments settled 15 to 30% longer than groomed.

My build

Ported the five passes to plain JavaScript on typed arrays and drew the strands with three.js as camera-facing ribbons.
6,000 strands x 16 particles = 96,000 particles. Two steps of 0.0167 s per frame. Per step: global x2, local x2, collision x1, length x2, with stiffness 30, 40 and 25, all from the repo's prefab.
With shape and collision on, at most 39 of the 90,000 free particles sat more than 2 mm inside a sphere on the frames I sampled.
The head, the groomed style and the shading are procedural and mine. The clip then drops both shape stiffness values to 0.3 and 0.08 of the prefab and raises them again.

Trade-offs

Strands do not collide with each other, so the volume is only what the groom gives. Colliders are spheres only here. No momentum means no swing. The repo calls itself an educational project that is not ready for production, and part of its simulation is based on AMD's TressFX.

When to use it

A readable reference for how TressFX-style constraints fit together, or short stylised hair. For long hair that must swing, the integration pass needs its velocity term back first.

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

Source and credits

Technique source: VHair by kennux (Kenneth Ellersdorfer), https://github.com/kennux/VHair, MIT license, commit 6a07ad19812a4de2cef132c07f4f1db6d7cd0894. The repo describes itself as an educational project that is not usable in production. Ported to JavaScript from its CPU TressFX Burst passes: CPUTFXVerlet_Burst, CPUTFXGlobalShapeConstraint_Burst, CPUTFXLocalShapeConstraints_Burst (including CalculateTransforms), CPUTFXSphereCollision_Burst and CPUTFXLengthConstraints_Burst, with the pass order, iteration counts and stiffness values of its CPU_Body_TressFX_Burst prefab.

Based on: TressFX by AMD (GPUOpen), https://github.com/GPUOpen-Effects/TressFX, MIT license. Part of VHair's simulation code is based on TressFX (see LICENSE_TRESSFX in the VHair repo).

Render framework: three.js by mrdoob and contributors, https://github.com/mrdoob/three.js, MIT license, r187. Used for drawing and lighting. It is not the source of the hair technique.

My own code: the procedural head and groomed style, the ribbon strand drawing and hair shading, the head motion, the debug view, camera moves, labels and video. No external models or assets. The repo's hair assets and models are not used.