This version keeps the integration small so the moving parts are visible. It uses the WebGPU / TSL path from `three-fluid-fx/tsl`, where the effect is built with RenderPipeline output nodes, TSL factories, and WGSL compute helpers.
Live demo
What this demo teaches
Instead of running an expensive physics simulation, we sample the fluid flow directly in the vertex shader. The fluid acts like a real-time magnetic wind that stretches, twists, and displaces the rigid shape only where the cursor touches it, instantly snapping back when the wind dies. In this variant, the relevant fluid output is TextureNode fields: velocityNode, densityNode, and dyeNode when dye is enabled.
- Understand the difference between procedural particles and GPGPU particles.
- Use fluid flow as render-time displacement instead of integrated motion.
- Keep the base shape readable while still reacting to the cursor.
Implementation path
- Generate the base shape from instance id
The knot formula gives every particle a deterministic home position. There is no position texture to update.
- Sample the fluid at the projected point
The shader uses the particle position to find the matching screen-space fluid sample.
- Blend flow into displacement
Threshold and range controls decide when the brush is strong enough to bend the shape.
- Minimal scope
It avoids GUI state and preset switching. Read it first when you need the smallest reliable version of the technique.
const particles = createTrefoilParticles(fluid.velocityNode, {
count: 4000,
displacement: 0.34,
})
renderer.setAnimationLoop(() => {
fluid.step(dt)
particles.update({ elapsed, dragStrength, maxFlowSpeed })
renderer.render(scene, camera)
}) Parameters to tune
| Parameter | What it controls | How to tune it |
|---|---|---|
displacement | Maximum offset added to the procedural position. | Raise until the cursor is visible. Lower it if the knot stops reading as a knot. |
dispThreshold | Minimum fluid activity before particles move. | Increase it to ignore background noise and keep the shape stable. |
dispRange | Softness of the transition above the threshold. | Wider ranges feel organic. Narrow ranges feel graphic and sharp. |
dragStrength | Extra pull along screen-space flow. | Use small values. Too much drag turns the procedural object into a smear. |
Source landmarks
Start from examples/tsl/minimal/particles-trefoil/main.ts. These are the parts worth reading first:
- The trefoil position formula.
- Fluid sampling in the vertex or TSL node path.
- Threshold and displacement controls.
- Point size and rotation updates.
Production notes
- Use procedural particles when the object has a strong identity: a knot, logo, text, shell, or mask.
- Do not add GPGPU state unless the particles need memory.
- Clamp fast flow so quick pointer movements do not explode the shape.