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
Distortion doesn't paint colors; it bends light. The fluid's velocity field acts as a screen-space UV displacement map, warping the typography and 3D objects underneath it exactly where the cursor drags them. In this variant, the relevant fluid output is TextureNode fields: velocityNode, densityNode, and dyeNode when dye is enabled.
- Understand why distortion reads flow instead of drawing fluid color.
- Know how intensity, splatForce, and dissipation affect perceived refraction.
- Choose the right distortion family for heat haze, glass, chromatic smear, or water.
Implementation path
- Render a scene texture
The distortion needs pixels to refract. In WebGL that usually means an EffectComposer chain; in TSL it means a scene node in the RenderPipeline.
- Sample the fluid flow
The main signal is the velocity stored in the fluid output. Strong gestures create larger UV offsets.
- Clamp the visual gain
Distortion becomes ugly faster than overlays. Keep intensity modest and tune the solver force separately.
- Minimal scope
It avoids GUI state and preset switching. Read it first when you need the smallest reliable version of the technique.
import { FluidSimulation, simpleDistortion } from 'three-fluid-fx/tsl'
const fluid = new FluidSimulation(renderer)
pipeline.outputNode = simpleDistortion(
sceneNode,
fluid.densityNode,
0.08,
) Parameters to tune
| Parameter | What it controls | How to tune it |
|---|---|---|
intensity | Amount of UV displacement applied to the scene. | Start low. Raise until motion is visible but edges do not tear. |
splatForce | How much pointer motion enters the velocity field. | Use force for interaction feel, then use intensity for final visual scale. |
velocityDissipation | How long the refractive motion keeps moving. | High values feel like glass or water. Lower values feel like quick heat shimmer. |
densityDissipation | Mask lifetime for density-aware distortion styles. | Raise it for water and chromatic effects that use density as a height or visibility mask. |
Source landmarks
Start from examples/tsl/minimal/distortion/main.ts. These are the parts worth reading first:
- Composer or RenderPipeline setup.
- Which distortion style is selected.
- The shader or node path that turns flow into UV offsets.
- Time uniform updates for animated water and caustics.
Production notes
- Keep distortion below the threshold where text becomes unreadable.
- For UI overlays, prefer heat-haze intensity over strong chromatic split.
- For water, use larger splats and slower density decay so the surface has body.