FaceFall
- 5 Devlogs
- 37 Total hours
Cool cloth simulation toy
Cool cloth simulation toy
This update was mostly about adding interactive features.
The biggest one was cloth tearing. My first attempt never actually worked because the strain limiter prevented springs from stretching far enough to snap. The fix was checking the stretch before the limiter ran, then making sure tearing also removed the connected shear and bend springs so pieces actually separated instead of hanging by invisible links.
I also added:
The ball ended up being my favourite feature. Throwing it through a pinned curtain now rips a hole through the fabric, the stress heatmap lights up around the impact, and the cloth keeps simulating normally afterwards.
Everything was backed up with new self-tests for tearing and ball collisions, and the original cloth behaviour stayed identical when the new features were disabled.
This update was all about making the simulation feel physically believable instead of just stable. Some things worked exactly as I’d hoped, some didn’t, and one bug ended up explaining why cloth doesn’t really bounce.
The old simulation only used simple linear drag, which meant the whole cloth slowed down evenly in every direction. It worked, but everything fell like it was moving through syrup.
I replaced that with per-triangle aerodynamic forces. Every triangle now generates drag based on its orientation and velocity, so the cloth behaves much more naturally. A flat sheet catches the air like a parachute, while an edge-on sheet slices straight through it.
The drag is also normalised so changing the cloth resolution doesn’t completely change the way it falls.
The difference is surprisingly noticeable. Around the one second mark the cloth now billows into a canopy before landing, something the old solver never managed.
Another thing I wanted was proper self collision.
Previously, folds could pass straight through each other. I implemented a spatial hash so nearby particles can efficiently detect overlaps without checking every possible pair, keeping the cost close to linear even on larger simulations.
The first time I tried self collision a while ago it was unstable because I only corrected positions. This time every collision also updates particle velocity, so the solver no longer fights itself.
Folded cloth now stacks into proper layers instead of collapsing through itself.
I also rewrote the collision response.
Instead of instantly pushing particles outside the collider, objects now behave like spring-damper surfaces. The cloth compresses slightly into the collision shell before settling, which produces much smoother landings and completely removed the tiny jitter I was previously working around.
This ended up being the most interesting part of the update.
I originally wanted the cloth to bounce realistically, so I tried several different approaches. None of them looked right.
While debugging I found something strange. After impact, some particles still had positive upward velocity for frame after frame, yet they never actually moved.
The culprit was strain limiting.
Every correction was changing particle positions without updating their velocities, meaning momentum was effectively disappearing from the simulation. Once I applied the matching velocity correction alongside each positional fix, impacts started travelling naturally through the cloth as visible waves instead of vanishing.
Ironically, fixing that bug also answered the original question.
Real cloth barely bounces.
Once the sheet starts draping over a sphere, the hanging fabric absorbs almost all of the remaining energy. Even with perfect restitution the cloth only lifted a few centimetres before settling again.
Because of that, the default fabric presets now use very low restitution values. Silk barely rebounds at all, while leather keeps a little more energy.
The simulator now runs internally at 120 FPS.
Preview mode still displays at 60 FPS by stepping the simulation twice per frame, while exports run at the full rate. Renders take longer, but the smoother motion is absolutely worth it, especially during impact.
This update made the simulation feel much closer to real fabric.
The cloth now catches the air properly, folded layers no longer pass through each other, collisions are smoother, and the behaviour after impact looks far more convincing than before.
It also reminded me that sometimes the most realistic result isn’t the one you expect. After spending hours trying to make the cloth bounce, the correct answer turned out to be that real cloth… mostly doesn’t.
After trying a few more “realistic” cloth techniques, I realised I’d made the simulation worse instead of better.
The biggest problems were all my own:
Sometimes the simplest solution really is the right one.
I rolled the simulator back to the model that was already producing the best results:
I removed self-collision and the angle-based bending entirely. Fabric presets now just adjust the bend spring stiffness, so materials like silk and leather still behave differently without adding unnecessary complexity. The settings menu was cleaned up to match.
The rendering improvements all stayed, including the updated shader, high-resolution offscreen exports (1080p, 1440p and 4K), and the quality-of-life improvements to the presets and UI.
I also added a --selftest mode that automatically runs a standard cloth drop onto the sphere and checks that the simulation stays stable. It verifies that particle positions remain finite, the cloth doesn’t explode or fling itself away, and structural springs stay within the configured strain limit.
This update started with one simple bug report:
“the cloth barely renders and it keeps clipping into the model.”
It turned out to be three completely different problems.
By the end of today I’d rebuilt the renderer, rewritten parts of the collision system, doubled the maximum cloth resolution to 128x128, added adjustable cloth and collider sizes, and accidentally made the entire simulation explode into NaNs.
At first I assumed the renderer or lighting was broken.
It wasn’t.
The real problem was raylib’s batching. I disabled backface culling before submitting the cloth geometry, but re-enabled it before the batch was actually flushed to the GPU. The simulation itself was perfectly fine, but almost the entire cloth was getting backface-culled before it ever reached the screen.
The fix was simply flushing the render batch before and after changing the GPU state so the cloth was actually drawn with culling disabled.
While I was there I also rewrote the lighting. Instead of flat shading per quad, the cloth now generates smooth per-vertex normals and uses simple Gouraud shading. The difference is huge, especially on higher resolutions.
The second issue was much harder.
The first problem was that my collision shell was far too thin. At lower resolutions the particles stayed outside the collider, but the rendered surface between them could still cut straight through the mesh. I fixed that by scaling the collision shell with particle spacing so coarse cloth stays slightly further away from the surface.
That solved part of it.
The bigger issue only showed up against thin meshes. Sometimes a particle could move completely through the object in a single step, and because the collision system only looked at the particle’s final position it would happily push it out of the wrong side.
The solution was switching to continuous collision detection. Every particle now stores its previous position, and I sweep that movement against the mesh instead of only checking where it ended up.
My first implementation of continuous collision looked perfect.
For about three seconds.
Then the cloth completely disappeared.
It turned out I had created an energy feedback loop where particles bounced between both sides of thin geometry until the simulation exploded into NaNs.
Instead of pushing particles to a new position after a collision, I now simply restore their previous valid position and remove the inward velocity. It’s slightly more conservative, but completely stable.
With collision working properly again I increased the maximum cloth resolution from 64x64 to 128x128.
That pushed the simulation to over 16,000 particles, nearly 100,000 springs, and roughly 32,000 rendered triangles.
To keep performance reasonable I added a simple spatial grid for mesh collisions so particles only test nearby triangles instead of the entire mesh every substep.
The result is still effectively real-time, even at the highest resolution.
The settings menu also picked up a couple of new options:
Changing either one rebuilds the simulation immediately, updates the collision data, and automatically reframes the camera so everything stays visible.
FaceFall is finally starting to feel like a proper tool rather than just a physics demo.
The renderer looks significantly better, collisions are much more reliable, larger simulations run comfortably, and exporting deterministic comparison clips is now almost entirely hands-off.
There’s still plenty left to do, especially around self-collision, but it’s getting much closer to the project I originally imagined.
I started FaceFall because I kept seeing those short videos where a cloth drops over the same object at different mesh resolutions, but I don’t know how to use things like Blender to make them myself. The difference between a 2x2 sheet and a dense mesh is surprisingly satisfying, and I wanted to build my own version from scratch instead of relying on an existing physics engine.
The goal isn’t just to make another cloth simulator. The mesh density is the whole point. At really low resolutions the cloth behaves more like a stiff piece of cardboard, while higher resolutions start producing proper folds, ripples, and believable draping.
Reference: https://www.youtube.com/shorts/s_G2gBbWYF0
So far I’ve built a complete cloth simulator in a single C99 source file using raylib.
Current features include:
At this point it’s less of a physics experiment and more of a small content creation pipeline. I can recreate the exact same simulation with different resolutions and export identical camera angles for comparison videos.
The biggest challenge early on was stability. Increasing spring stiffness quickly caused the simulation to explode, so I ended up basing the number of simulation substeps on the spring frequency instead of using a fixed timestep.
The cloth also stretched far too much at first and looked more like rubber than fabric. Adding strain limiting made a huge difference and stopped the structural springs from over-extending.
Collision was easily the hardest part. Spheres and cubes weren’t too bad, but arbitrary OBJ meshes became a rabbit hole of closest-point tests, broad-phase checks, collision response, and trying to stop particles tunnelling straight through thin geometry.
I also spent longer than I’d like fighting Windows. Exporting raw frames through ffmpeg would randomly corrupt the output until I realised _popen needed to be opened in binary mode.
The biggest thing I’m still working through is sphere collision.
Most of the time it behaves exactly how I’d expect, but every now and then parts of the cloth clip into the sphere instead of settling over the surface. Once a few particles end up inside the collider the cloth starts rendering incorrectly, so it’s pretty obvious when it happens.
I’m fairly confident the issue isn’t the spring solver anymore. The collision detection itself is finding the contacts, but the positional correction isn’t always pushing particles completely back outside the sphere. That’s the main thing I want to fix before moving on.
Once the collision is reliable I want to polish the visuals and start generating comparison clips at different mesh densities.
That’s really the whole reason I started this project. Seeing the exact same cloth drop change from a rigid, blocky sheet into something that actually looks like fabric is surprisingly satisfying, and now I finally have a tool that lets me create those videos however I want.