Fluid Image Advection
Melt Your Image Into Paint With a Real Fluid Simulation
- image
- mask
- image
- velocity_preview
Fluid Image Advection distorts an image by literally pushing its pixels through a simulated fluid. Not a blur, not a warp filter with a clever name - a real velocity field, evolving over time, carrying color with it. Feed it a portrait and you get oil-paint swirling. Feed it a landscape and you get a marbled, ink-in-water mess you'd never get from a fixed warp. It's from the same stable-fluids family as the pack's smoke nodes, minus the buoyancy: pure viscous flow, stirred by random forces.
The algorithm is worth knowing because it explains every slider on the node. Each step: a few random forces ("sticks") jab the velocity field, the field optionally diffuses and gets vorticity confinement applied (so the motion curls instead of going straight), velocity advects itself, damping bleeds energy out, and finally the image color field is advected through the resulting velocity. Repeat for steps. Semi-Lagrangian advection is what keeps the whole thing stable at the big time steps these nodes use.
The inputs that actually matter:
stepsanddt- how long and how big the time steps are. More steps, more smear.resolution_scale- the sim runs internally at this fraction of image resolution. Lower = faster and chunkier. This is your speed dial.force_count,force_strength,force_radius- how many sticks jab per step, how hard, and how wide. More or harder forces = more violent deformation.swirl_strength- a rotational component added around each stick, which is what makes the flow curl.velocity_damping- lower values settle the flow faster (more viscous look). 0.98 default means it keeps sloshing.vorticity- curl enhancement on top. This is the "make it billow" control.wrap_mode- how advection samples outside the image:clamp,wrap, ormirror.wrapgives that seamless infinite-texture look.
It returns the distorted image plus a velocity_preview image so you can see the flow field - genuinely useful for understanding why the deformation went somewhere weird. An optional mask input limits the distortion to masked areas, which is how you turn "whole image melting" into "this one region is melting."
Where it fits
Two classic uses: (1) as a stylization stage - run it on a render, then img2img at low denoise so the model re-interprets the smears as paint, smoke, or water instead of just noise; (2) as a motion source for animation - the velocity field is deterministic per seed, so you can re-run with different steps or interpolate force_strength to fake an evolving flow. There's also a Latent variant of this exact node if you want the deformation to happen pre-decode.
Installing
Part of the Skoogeer-Noise pack by ttulttul:
cd ComfyUI/custom_nodes
git clone https://github.com/ttulttul/Skoogeer-Noise
Restart, or install via ComfyUI Manager ("Skoogeer-Noise"). Deps are just torch/numpy/einops/pyyaml - no models, no downloads, pure math.
Gotchas
The number-one surprise is speed. This is a real simulation; a full-res image with steps in the hundreds will take a while, and it runs on CPU tensors. Drop resolution_scale to 0.5 and don't be shy about it. Second: zero force_count means a static image with extra steps - if nothing happens, check that forces are actually being injected. And if your edges go weird, that's wrap_mode doing its job; mirror is the flattering option for most images. Keep the folder named Skoogeer-Noise or the import breaks - the module path is custom_nodes.Skoogeer-Noise.
Inputs (14)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | Image to distort by fluid advection. | |
| steps | INT | 200–256 | Number of simulation steps. |
| dt | FLOAT | 1.000–4 | Time step size used during semi-Lagrangian advection. |
| resolution_scale | FLOAT | 0.500.25–1 | Internal simulation resolution scale (lower = faster). |
| force_count | INT | 30–64 | Number of random force injections ('sticks') per step. |
| force_strength | FLOAT | 5.00–256 | Linear force magnitude added to velocity (in pixels / step). |
| force_radius | FLOAT | 0.100–1 | Normalized radius of influence (0.1 = 10% of min(width,height)). |
| swirl_strength | FLOAT | 2.00–256 | Rotational component added around each stick position. |
| velocity_damping | FLOAT | 0.9800–1 | Velocity decay applied each step (lower = more viscous / quickly settles). |
| diffusion | FLOAT | 0.000–1 | Optional velocity diffusion (simple Gaussian smoothing). |
| vorticity | FLOAT | 0.000–10 | Vorticity confinement strength (adds swirling detail). |
| seed | INT | 00–18446744073709550000 | Seed controlling the random stick injections. |
| wrap_mode | COMBO | clamp | How advection samples outside the bounds. |
| maskopt | MASK | Optional mask to limit the effect to masked areas (mask is resized to image resolution). |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| velocity_preview | IMAGE | — |