# Hair Simulation WebGPU

**URL:** <https://forum.babylonjs.com/t/hair-simulation-webgpu/55663>\
**Category:** Demos and projects\
**Tags:** wgsl, compute-shader, constraint, verlet-integration, rope\
**Created:** [December 31, 2024, 8:01pm UTC](https://forum.babylonjs.com/t/hair-simulation-webgpu/55663 "2024-12-31T20:01:13Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Propolisa](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/propolisa/32/40423_2.png) [@Propolisa](https://forum.babylonjs.com/u/Propolisa)\
**Post date:** [December 31, 2024, 8:01pm UTC](https://forum.babylonjs.com/t/hair-simulation-webgpu/55663/1 "2024-12-31T20:01:13Z")

</div>

Hey folks happy new year!

For a [very WIP](https://propolisa.github.io/pisces/) underwater first-person-fishing game with a currently very hairless character, I wanted non-rigid hair.

But of course it will also be useful for various soft elements like floating seaweed, fish fins, kelp. So I wanted to have performant rope physics that I can slap onto Babylon meshes with some control over the physics settings.

So, down the rabbit hole with ChatGPT to learn some simulation math, then implement it in WGSL.

Here’s the result!

> **[Babylon.js Playground](https://playground.babylonjs.com/?webgpu#3URR7V%23366)**
>
> Babylon.js playground is a live editor for Babylon.js WebGL and WebGPU 3D scenes

 ![It's not a demo of how to render hair, no idea how to do that yet ;)](https://us1.discourse-cdn.com/flex024/uploads/babylonjs/original/3X/0/6/061729fa98567c760758764319a955607262b58e.jpeg)

I am working in whatever midnight minutes I can spare (new dad) so it is not as polished as I would like, but I would love to get feedback on any part 🙂 Especially some parts of the physics aren’t quite natural. I will add some questions for the matrix geniuses here when I have the energy. 🙂

Cheers!

---

<div class="post-metadata">

**Author:** ![sebavan](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/sebavan/32/57_2.png) [@sebavan](https://forum.babylonjs.com/u/sebavan)\
**Post date:** [December 31, 2024, 8:20pm UTC](https://forum.babylonjs.com/t/hair-simulation-webgpu/55663/2 "2024-12-31T20:20:11Z")

</div>

This is awesome !!!

---

<div class="post-metadata">

**Author:** ![regna](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/regna/32/23915_2.png) [@regna](https://forum.babylonjs.com/u/regna)\
**Post date:** [December 31, 2024, 8:22pm UTC](https://forum.babylonjs.com/t/hair-simulation-webgpu/55663/3 "2024-12-31T20:22:51Z")

</div>

Whoa!!

---

<div class="post-metadata">

**Author:** ![Propolisa](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/propolisa/32/40423_2.png) [@Propolisa](https://forum.babylonjs.com/u/Propolisa)\
**Post date:** [January 4, 2025, 1:53am UTC](https://forum.babylonjs.com/t/hair-simulation-webgpu/55663/4 "2025-01-04T01:53:18Z")

</div>

I’ve improved the stability and performance, 🙂

> **[Babylon.js Playground](https://playground.babylonjs.com/?webgpu#3URR7V%23370)**
>
> Babylon.js playground is a live editor for Babylon.js WebGL and WebGPU 3D scenes

 ![image](https://us1.discourse-cdn.com/flex024/uploads/babylonjs/original/3X/e/9/e9b7531e3102318698939f21573d77c6bcd42cc6.jpeg)

And a WIP fork for simulating simple plants with thin-instanced ‘leaves’:

> **[Babylon.js Playground](https://playground.babylonjs.com/?webgpu#3URR7V%23374)**
>
> Babylon.js playground is a live editor for Babylon.js WebGL and WebGPU 3D scenes

 ![image](https://us1.discourse-cdn.com/flex024/uploads/babylonjs/original/3X/1/5/154e4e6b1d4ad6030198c082c3bde94faa0d87a6.jpeg)

---

<div class="post-metadata">

**Author:** ![simon](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/simon/32/45590_2.png) [@simon](https://forum.babylonjs.com/u/simon)\
**Post date:** [January 5, 2025, 1:27am UTC](https://forum.babylonjs.com/t/hair-simulation-webgpu/55663/5 "2025-01-05T01:27:28Z")

</div>

Amazing! 😍

---

<div class="post-metadata">

**Author:** ![One\_Flag](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/one_flag/32/23226_2.png) [@One\_Flag](https://forum.babylonjs.com/u/One_Flag)\
**Post date:** [January 6, 2025, 10:56am UTC](https://forum.babylonjs.com/t/hair-simulation-webgpu/55663/6 "2025-01-06T10:56:13Z")

</div>

This is awesome 😍  
Great work!

Congrats on becoming a parent. Welcome to the club 🤘

---

<div class="post-metadata">

**Author:** ![k\_merino](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/k_merino/32/29498_2.png) [@k\_merino](https://forum.babylonjs.com/u/k_merino)\
**Post date:** [January 7, 2025, 9:40am UTC](https://forum.babylonjs.com/t/hair-simulation-webgpu/55663/7 "2025-01-07T09:40:07Z")

</div>

The development is looking great! Nice improvements on the hair dynamics.The plant life will be a perfect addition to your game development. Maybe you can implement bubble plumes coming from the ocean floor and instance baby bottles as the particles! 😉

---

<div class="post-metadata">

**Author:** ![Propolisa](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/propolisa/32/40423_2.png) [@Propolisa](https://forum.babylonjs.com/u/Propolisa)\
**Post date:** [January 12, 2025, 11:15pm UTC](https://forum.babylonjs.com/t/hair-simulation-webgpu/55663/8 "2025-01-12T23:15:20Z")

</div>

Thanks for the kind feedback! ❤ Happy to get criticism too 🙂

I managed to generate frame-by-frame some _solid curves_ too now, rather than using line system, which was nice for debugging but not possible to shade:

> **[Babylon.js Playground](https://playground.babylonjs.com/?webgpu#3URR7V%23383)**
>
> Babylon.js playground is a live editor for Babylon.js WebGL and WebGPU 3D scenes

There’s a glitch in the hermite interpolation for now but it had an awesome mechanical look so I left it! 🙂

 ![image](https://us1.discourse-cdn.com/flex024/uploads/babylonjs/original/3X/8/6/86865bf0f81eb97c21a97cca38f26ca1caa4b62b.jpeg)

Edit: More realistic stiffness implementation:

> **[Babylon.js Playground](https://playground.babylonjs.com/?webgpu#3URR7V%23385)**
>
> Babylon.js playground is a live editor for Babylon.js WebGL and WebGPU 3D scenes

---

<div class="post-metadata">

**Author:** ![Deltakosh](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/deltakosh/32/26635_2.png) [@Deltakosh](https://forum.babylonjs.com/u/Deltakosh)\
**Post date:** [January 13, 2025, 5:54pm UTC](https://forum.babylonjs.com/t/hair-simulation-webgpu/55663/10 "2025-01-13T17:54:43Z")

</div>

So good!

---

<div class="post-metadata">

**Author:** ![zertaul](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/zertaul/32/37493_2.png) [@zertaul](https://forum.babylonjs.com/u/zertaul)\
**Post date:** [January 14, 2025, 3:19am UTC](https://forum.babylonjs.com/t/hair-simulation-webgpu/55663/11 "2025-01-14T03:19:49Z")

</div>

Put an eye in it and learn from the boss slowly

---

<div class="post-metadata">

**Author:** ![Propolisa](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/propolisa/32/40423_2.png) [@Propolisa](https://forum.babylonjs.com/u/Propolisa)\
**Post date:** [January 14, 2025, 11:54pm UTC](https://forum.babylonjs.com/t/hair-simulation-webgpu/55663/12 "2025-01-14T23:54:47Z")

</div>

It’s time for the best part! I am working to add hair to an animated character now. I’m using a Blender + Mixamo + GLSL export workflow as I’ve seen from examples.

Unfortunately the transform nodes + bone matrices are really doing my head in (it is a very weak head in the mathematical sense haha).

In too much detail (helps me understand what I’m trying to solve as I write it), here I’ll explain my mental model for the simulation interacting with Babylon scene:

# World space:

- the hair mesh has its parent set to the character mesh (or transform node); so all world transforms to the character applies to the hair too. BUT importantly we record the **inverse delta** of world space transform each iteration. (this is for preserving physical _locality_, see explanation at bottom).

# Local space:

Each hair strands’ **root point** is pinned strictly to some vertex in the character mesh – calling it **spawn vertex** – it doesn’t follow physics. Other points down the strand, contrarily, should only move globally because of simulated physical constraints.

## Root point behavior: statically track the spawn vertex

- That means at the beginning of each sim iteration we set the local position of the hair **root point** to the _local space position_ of its **spawn vertex**. So significant local space transforms (e.g. bones, morph targets) affect both the spawn vertex and root point equivalently.

## Other points’ behavior: follow physical constraints

- For non-root points in the strand, **only simulated constraints** should determine motion _in world space_. But there’s a gotcha – Because we’re using local space coordinates for the **root points’ positions** and using parenting for the global transforms, when we drag the character the hair would just move with it statically. That doesn’t respect physical notion of **locality** where the individual sections of hair each want to stay where they are until some external force tries to move them (e.g. tension upstream on the strand). So we need to do a trick here – for all non-root points, offset their positions _against_ the mesh’s global transform (more specifically, offset by the inverse of the delta transform between the previous frame / iteration and current), causing them to appear suspended in space by resisting being moved by the parent mesh (until one-way constraints to the statically positioned root points cause them to be pulled towards the mesh’s new global position). This offsetting is done without impact to acceleration (energy shouldn’t be added because no movement occurs in global space).

To try to summarize this part, I wanna keep the simulation positions in local space (preserves numeric fidelity) but to have global motion affect the simulation accurately we need to offset all _sim-controlled_ (e.g. non-root) hair points against any mesh transforms so they can _organically_, rather than statically, react to the transform.

# tl;dr; Open question

@Evgeni_Popov perhaps? 🙏

**What’s a viable approach to get the final post-transformation (e.g. after bones, morph targets) vertex positions / normals into my compute shader?** I had the idea to just write the computed positions to a storage buffer from a material plugin custom vertex stage ([playground](https://playground.babylonjs.com/?webgpu#BCNJD4#42) of that humiliation); of course it’s not possible to write from a vertex shader (if one could only know everything beforehand)!

My next effort would probably be to port the transformations section of the (compiled) [default vertex shader WGSL](https://github.com/BabylonJS/Babylon.js/blob/84580a00705d4513393988e3eb2cab0f2d9053b3/packages/dev/core/src/Shaders/default.vertex.fx#L2) to a compute shader. But perhaps there’s a better solution?

If it would simplify the problem, for my own “character hair” use case I could decide by definition that only one bone has full weight over the relevant spawn vertices, and that could be the sole basis of local space transforms (no morph targets etc). Maybe in that case I could go with an approach like using `{ headBone }.getAbsoluteMatrix()` as a uniform. But I wasn’t able to pull that off in this test playground yet, likely because I don’t understand some interactions between the mesh parenting and skinning.

Thanks for reading!

---

<div class="post-metadata">

**Author:** ![CodingCrusader](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/codingcrusader/32/29857_2.png) [@CodingCrusader](https://forum.babylonjs.com/u/CodingCrusader)\
**Post date:** [January 15, 2025, 11:48am UTC](https://forum.babylonjs.com/t/hair-simulation-webgpu/55663/13 "2025-01-15T11:48:05Z")

</div>

May be this can help you find the correct position of the vertices in an animation:

> [@Coding Crusader's](https://forum.babylonjs.com/t/coding-crusaders/44765/36):
>
> [image] [Points of an animated mesh](https://playground.babylonjs.com/#SAE2GR)

---

<div class="post-metadata">

**Author:** ![Propolisa](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/propolisa/32/40423_2.png) [@Propolisa](https://forum.babylonjs.com/u/Propolisa)\
**Post date:** [January 15, 2025, 1:33pm UTC](https://forum.babylonjs.com/t/hair-simulation-webgpu/55663/14 "2025-01-15T13:33:17Z")

</div>

Nice, thanks 🙂 great simple POC and should solve my use case perfectly! I think CPU side is just fine for now actually, I will give your approach a try hopefully tonight.

I am still curious what a GPU-side solution would look like, especially for high-resolution meshes and with different types of local transforms I could imagine a performance difference getting more dramatic.

But it is good to be pragmatic as a solo dev, I am trying to embrace that 🙂

---

<div class="post-metadata">

**Author:** ![Propolisa](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/propolisa/32/40423_2.png) [@Propolisa](https://forum.babylonjs.com/u/Propolisa)\
**Post date:** [January 16, 2025, 12:06am UTC](https://forum.babylonjs.com/t/hair-simulation-webgpu/55663/15 "2025-01-16T00:06:55Z")

</div>

WIP, based on @CodingCrusader’s POC (thanks again!) I added ability to select spawn vertices based on a control mesh (for something that looks more like a hair pattern – previously I was just using a Y-clip value). I will integrate the positions into my simulation compute shader via storage buffers 🙂

 ![image](https://us1.discourse-cdn.com/flex024/uploads/babylonjs/original/3X/b/b/bbe3ec1aa22b58c92ef2764739edcad9dcc5842f.png)

> **[Babylon.js Playground](https://playground.babylonjs.com/#SAE2GR%231)**
>
> Babylon.js playground is a live editor for Babylon.js WebGL and WebGPU 3D scenes

I have some more questions meanwhile:

- I can notice a little bit of wobble in the computed positions relative to the source mesh, in both the original. Is this just numerical stability issues, or possibly the matrix data is one frame behind due to the timing of the updates?
- How should I scale / rotate an imported complex mesh with skeletons / animations such that this playground (using particles to depict the transformed positions) would still work? (I’ve read a few docs on this but did not completely understand it yet).

My own hair sim generates a single mesh, so I think I can just make that mesh have the root transform node of the character as its parent, then it should follow any transforms, I guess. I’ll try that next 🙂

---

<div class="post-metadata">

**Author:** ![Evgeni\_Popov](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/evgeni_popov/32/4432_2.png) [@Evgeni\_Popov](https://forum.babylonjs.com/u/Evgeni_Popov)\
**Post date:** [January 16, 2025, 10:14am UTC](https://forum.babylonjs.com/t/hair-simulation-webgpu/55663/16 "2025-01-16T10:14:25Z")

</div>

> [@Propolisa](#):
>
> What’s a viable approach to get the final post-transformation (e.g. after bones, morph targets) vertex positions / normals into my compute shader?

You can take inspiration from what we did for the `ComputeBoundingHelper` class, which had to do exactly that to calculate the right bounding box for a list of vertices:

- BoundingInfoHelper class: [Babylon.js/packages/dev/core/src/Culling/Helper/boundingInfoHelper.ts at master · BabylonJS/Babylon.js · GitHub](https://github.com/BabylonJS/Babylon.js/blob/master/packages/dev/core/src/Culling/Helper/boundingInfoHelper.ts)
- ComputeShaderBoundingHelper class: [Babylon.js/packages/dev/core/src/Culling/Helper/computeShaderBoundingHelper.ts at master · BabylonJS/Babylon.js · GitHub](https://github.com/BabylonJS/Babylon.js/blob/master/packages/dev/core/src/Culling/Helper/computeShaderBoundingHelper.ts)
- compute shader code: [Babylon.js/packages/dev/core/src/ShadersWGSL/boundingInfo.compute.fx at master · BabylonJS/Babylon.js · GitHub](https://github.com/BabylonJS/Babylon.js/blob/master/packages/dev/core/src/ShadersWGSL/boundingInfo.compute.fx)

---

<div class="post-metadata">

**Author:** ![CodingCrusader](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/codingcrusader/32/29857_2.png) [@CodingCrusader](https://forum.babylonjs.com/u/CodingCrusader)\
**Post date:** [January 17, 2025, 8:14pm UTC](https://forum.babylonjs.com/t/hair-simulation-webgpu/55663/17 "2025-01-17T20:14:58Z")

</div>

Did you know that you can attach a mesh to a bone? So you don’t need to calculate the animated positions

Here is an example:  
[attach mesh on a mesh | Babylon.js Playground](https://playground.babylonjs.com/#SAE2GR#3)

---

<div class="post-metadata">

**Author:** ![Propolisa](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/propolisa/32/40423_2.png) [@Propolisa](https://forum.babylonjs.com/u/Propolisa)\
**Post date:** [January 25, 2025, 11:59pm UTC](https://forum.babylonjs.com/t/hair-simulation-webgpu/55663/18 "2025-01-25T23:59:30Z")

</div>

This is exactly what I needed! I have adapted the WGSL compute setup (still very messy) to get updated vertex properties for hair roots. Here’s a POC you can never unsee (the code is even uglier than the abomination in the scene):

> **[Babylon.js Playground](https://playground.babylonjs.com/#AUWES6?webgpu)**
>
> Babylon.js playground is a live editor for Babylon.js WebGL and WebGPU 3D scenes

 ![image](https://us1.discourse-cdn.com/flex024/uploads/babylonjs/original/3X/3/9/39355f36afdef6756b8ade3e0db1c8fd7c24aae1.jpeg)

EDIT: I did observe that unlike in the Alien example, on my project’s character mesh some bones’ influences are off-kilter:

> **[Babylon.js Playground](https://playground.babylonjs.com/?webgpu#AUWES6%232)**
>
> Babylon.js playground is a live editor for Babylon.js WebGL and WebGPU 3D scenes

I deviated from the `ComputeShaderBoundingHelper` implementation in that I’m not updating the shader at all each frame (I’m setting buffers and bindings for bones only at the initialization time). Do you know off the top of your head if bone related buffers need to be updated each frame for the correct result (in which case I just got lucky with the alien, and need to add the per-frame updates)? In any case I will take another look next time I can work on this.

@CodingCrusader this is a great trick too, I think it factors only single bone (so, accurate for vertices with only one contributor bone) right? I think I can use it to good effect for adding spheroid-based collision to my physics sim (as the general head movement should be approximable with a single bone attachment).

---

<div class="post-metadata">

**Author:** ![Propolisa](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/propolisa/32/40423_2.png) [@Propolisa](https://forum.babylonjs.com/u/Propolisa)\
**Post date:** [May 22, 2025, 1:30am UTC](https://forum.babylonjs.com/t/hair-simulation-webgpu/55663/19 "2025-05-22T01:30:48Z")

</div>

I’m backk!

Here’s working hair on a character. So stoked 🙂  
 ![demo](https://us1.discourse-cdn.com/flex024/uploads/babylonjs/original/3X/7/7/777b2360ead952132d0c51155dd84c13a076a31d.gif)

Try it yourself in the playground - you can drag the character & change some params 🙂

> **[Babylon.js Playground](https://playground.babylonjs.com/?webgpu#AUWES6%2325)**
>
> Babylon.js playground is a live editor for Babylon.js WebGL and WebGPU 3D scenes

The code is very playground quality at the moment, I will work on a nice class to abstract adding hair / rope / tendrils onto stuff soon.

---

<div class="post-metadata">

**Author:** ![Evgeni\_Popov](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/evgeni_popov/32/4432_2.png) [@Evgeni\_Popov](https://forum.babylonjs.com/u/Evgeni_Popov)\
**Post date:** [May 22, 2025, 11:03am UTC](https://forum.babylonjs.com/t/hair-simulation-webgpu/55663/20 "2025-05-22T11:03:32Z")

</div>

Very beautiful!

---

<div class="post-metadata">

**Author:** ![AlitarSemiramis](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/alitarsemiramis/32/49920_2.png) [@AlitarSemiramis](https://forum.babylonjs.com/u/AlitarSemiramis)\
**Post date:** [May 24, 2025, 3:34am UTC](https://forum.babylonjs.com/t/hair-simulation-webgpu/55663/21 "2025-05-24T03:34:31Z")

</div>

Really nice demo! It was fun to play with all the sliders 😃

[Next page](https://forum.babylonjs.com/t/hair-simulation-webgpu/55663.md?page=2)
