Applying Physics to Code

Hair That Starts in Mid-Air

Another day, another debugging session with AI as we tracked down the root cause.


Me:

There’s something that’s been bothering me.

Hair strands are supposed to extend naturally, but sometimes they look like they’re starting halfway through instead of continuing from the original strand.

At first, I thought it was simply because there weren’t enough strands.


Claude

It could be a strand density issue…

But what if the strands aren’t actually missing?

Looking at the current structure, each strand is originally generated as one continuous path. It’s possible that the front portion is being removed by the mask clipping (destination-in), while only the extension (ext) remains visible.


That made me go back and look at the code again.

Structurally, every strand was indeed generated as one continuous path from start to finish.

So maybe the strand wasn’t actually being disconnected.

Maybe only the front part had disappeared.


Me

Wait…

Where is that strand even coming from?

From what I can see, it should be extending from the existing hair.

Why does it look like it’s detached?

Could it be that the starting point itself is wrong?


Claude

The starting point is most likely correct.

The real issue happens after the split point.

splitIdx is calculated by dividing origLen by STRAND_STEP_LEN (the physical distance of a single rendering step), which determines at which step the renderer reaches the original hair length.

The problem is that each step in traceStrand doesn’t move purely downward. Curl, wiggle, and orientation corrections all introduce sideways movement, so the actual movement distance (stepLen) is no longer the same as the vertical downward progress.

This becomes especially noticeable around the temples and side hair, where the hair mask is narrow and the boundaries are irregular. In those areas, a strand can drift slightly outside the mask before reaching the calculated split point.

When that happens, the portion of the base strand that falls outside the mask gets clipped away, while the ext section is still rendered normally.

To the user, it looks as if a completely new strand suddenly starts in mid-air.


Me

Alright.

Then let’s put a small red marker at the split point.


Claude

Sounds good.

A single red marker should be enough to verify the hypothesis immediately.

If the marker appears outside the mask, we’ll know the clipping is causing the issue.


Me

One more thing.

Right now each rendering step is basically linear, so whenever the step direction changes, it turns too sharply and struggles to maintain a consistent flow.

What if, instead, it behaved more like permed hair…

or even ramen noodles?

A series of gentle, repeating waves.


Claude

That’s a good idea.

The current curl implementation creates one large curve across the strand.

Replacing it with a repeating sine wave should produce much more natural-looking movement.


So we decided to implement both the diagnostic marker and the new wave-based curl model.


Solving Hair Flaring with Gravity

While we were at it, we also discussed another issue: hair strands flaring outward too much.


Me

The hair tips spread out too much.

In real life, hair gets pulled downward by its own weight as it gets longer.

What if we simulate a little bit of gravity?


Claude

That’s definitely possible.

The current code already has something similar to gravity:

restore = PI/2 - baseDir

It gradually pulls the strand back toward the downward direction.

The problem is that the restoring force is fixed at a constant value (0.15).

If a strand has already drifted outside the mask at a steep angle, that fixed force simply isn’t strong enough to pull it back.

Instead, we can preserve the original hair direction near the root,

and gradually increase the restoring force toward the tip,

allowing gravity to pull the strand downward more naturally as it gets longer.

It’s not a full physics simulation,

but visually, it should produce much more natural-looking hair.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *