Nodes/Skoogeer-Noise/Latent Smoke Simulation
ComfyUI Node

Latent Smoke Simulation

Dreamy Dissolves Before the VAE Even Runs

By ttulttul·Created 9 months ago·Updated 3 months ago· 14
Latent Smoke Simulation
  • latent
  • mask
  • latent
  • density_preview
  • velocity_preview
steps30
output_modefinal
dt1.00
resolution_scale0.50
force_count3
force_strength5.0
force_radius0.10
swirl_strength2.0
velocity_damping0.980
diffusion0.00
vorticity0.30
buoyancy1.50
ambient_updraft0.10
density_fade0.010
temperature_strength0.00
cooling_rate0.05
smoke_source_strength1.00
smoke_source_radius0.10
smoke_source_modeimage
seed0
wrap_modeclamp

Latent Smoke Simulation is the latent-space twin of Image Smoke Simulation, and the differences matter. Instead of smearing pixels, it treats your latent's channels as the smoke density and advects them through a buoyancy-driven velocity field. You run it on a LATENT, get a LATENT back, and only then decode. Because the deformation happens before the VAE, the smoke has a weirdly physical, material quality - it reads less like a Photoshop filter and more like the image was genuinely caught mid-dissolve.

The physics is the same stable-fluids core the whole pack uses: random force injections ("sticks"), vorticity confinement for the billowing curl, semi-Lagrangian advection, plus the smoke extras - buoyancy scaled by temperature, an ambient updraft, and a density field that fades. The difference is what's being transported. The image variant carries RGB color; this one carries the latent's feature channels, and it flattens them so the advection treats the whole sample as one field. If the latent has a noise_mask, that travels with it too.

The input list is long, and honestly most of it you'll leave alone:

  • smoke_source_mode - image derives density from the latent's own values, random injects puffs, mask injects from a MASK input (resized to latent resolution). Start with image.
  • steps - simulation steps, or per-step batch size when output_mode is batch. Batch mode is a genuinely fun trick here: you get every intermediate frame as an IMAGE, which makes a ready-made dissolve sequence for a video workflow.
  • buoyancy and vorticity - the two that turn "smear" into "smoke." Buoyancy pushes upward, vorticity makes the columns curl and billow.
  • density_fade - how fast the smoke thins out.
  • temperature_strength - inject heat so the smoke rises harder. Off by default.

Outputs are the distorted latent, plus density_preview and velocity_preview images so you can see what the sim decided to do without decoding just to check.

Where it fits

The most natural home for this is img2img: smoke the latent, then feed it to a KSampler at moderate denoise and let the model "heal" the deformation into something believable - drifting cloth, dissolving silhouettes, caustics. It also slots into the same slot as any of the pack's other perturbation nodes, and unlike them it produces motion, not texture. If your render's subject is static and you want implied wind, this is the node to reach for.

Installing

Part of the Skoogeer-Noise pack by ttulttul - one install gets the whole pack:

cd ComfyUI/custom_nodes
git clone https://github.com/ttulttul/Skoogeer-Noise

Restart ComfyUI (or install via Manager, search "Skoogeer-Noise"). Dependencies are torch, numpy>=1.26, einops, pyyaml; no models, no downloads beyond the repo. Keep the folder named exactly Skoogeer-Noise - the module path is custom_nodes.Skoogeer-Noise and a renamed directory breaks the import.

Gotchas

Two things bite people here. First, the sim runs at resolution_scale and on CPU tensors, so big latents with high steps are slow - drop resolution_scale to 0.5 (its default) and accept the chunkier flow. Second, if you're on Flux.2, its latents are patchified into 128 channels in 2x2 blocks, and advecting those directly produces blocky, wrong-correlation artifacts. Unpatchify with the pack's Unpatchify Flux.2 Latent before this node and Patchify Flux.2 Latent after. And if the smoke just doesn't move, check buoyancy - zero buoyancy plus zero updraft is a still image with extra steps.

Categorylatent/perturb

Inputs (23)

NameTypeDefaultDescription
latentLATENTLatent to simulate as smoke (buoyancy-driven advection).
stepsINT300–256Number of simulation steps (or batch size when output_mode='batch').
output_modeCOMBOfinalWhen 'batch', returns a batch of intermediate outputs after each step (1..steps).
dtFLOAT1.000–4Time step size used during advection.
resolution_scaleFLOAT0.500.25–1Internal simulation resolution scale (lower = faster).
force_countINT30–64Number of random force injections ('sticks') per step. When smoke_source_mode='random', also controls puff count.
force_strengthFLOAT5.00–64Linear force magnitude added to velocity (latent pixels / step).
force_radiusFLOAT0.100–1Normalized radius of influence for stick forces (0.1 = 10% of min(width,height)).
swirl_strengthFLOAT2.00–64Rotational component added around each stick position.
velocity_dampingFLOAT0.9800–1Velocity decay applied each step (lower = more viscous / quickly settles).
diffusionFLOAT0.000–1Optional velocity diffusion (Gaussian smoothing).
vorticityFLOAT0.300–10Vorticity confinement strength (adds billowing/swirl detail).
buoyancyFLOAT1.500–10Strength of upward buoyant force.
ambient_updraftFLOAT0.100–10Constant upward airflow added each step.
density_fadeFLOAT0.0100–1Density dissipation per step (higher = smoke dissipates faster).
temperature_strengthFLOAT0.000–10Initial temperature injection per step (hotter smoke rises more).
cooling_rateFLOAT0.050–1Temperature decay per step.
smoke_source_strengthFLOAT1.000–10Amount of smoke density injected each step.
smoke_source_radiusFLOAT0.100–1Radius of smoke injection (normalized). Used as puff radius in random mode; softens the source in image/mask modes.
smoke_source_modeCOMBOimageSmoke source mode: 'image' derives density from the input latent; 'random' injects random puffs; 'mask' injects from a MASK input.
seedINT00–18446744073709550000Seed controlling random forces and random sources.
wrap_modeCOMBOclampHow advection samples outside the bounds.
maskoptMASKUsed when smoke_source_mode='mask' to define the emission region (resized to latent resolution).

Outputs (3)

NameTypeDescription
latentLATENT
density_previewIMAGE
velocity_previewIMAGE