ComfyUI Node

Heat Haze

Cheap shimmer that sells a hot-plate look

By criskb·Created 7 months ago·Updated 5 months ago· 0
Heat Haze
  • image
  • mask
  • image
  • mask
  • heat_haze_info
settings_json{"direction":"up","strength_px":8.0,"scale":3.2,"phase_deg":0.0,"turbulence":0.55,"edge_falloff":0.35,"chroma_split_px":0.8,"mix":1.0,"mask_feather":6.0,"invert_mask":false}

Heat Haze is the MKRShift node you reach for when you want that wobbly, mirage-over-asphalt shimmer on a frame and you don't want to animate a displacement map by hand. It's a deterministic distortion pass - no model, no API, no seed lottery - and it runs in milliseconds. If you've ever added "heat haze" to a prompt and gotten a smeary artifact instead, this is the honest version of that idea: you generate the clean image, then let this node fake the atmosphere on top.

It lives in MKRShift Nodes/VFX/Distortion, part of the broader color/VFX finishing family in the pack. The whole category is "finish the frame with deterministic pixel ops," which is exactly the layer the KB's post-processing doc argues people under-use - you don't need a diffusion re-pass to add atmospheric shimmer.

How it works

The node displaces pixels through a procedural wave field instead of a real fluid sim. It builds a base grid, then layers a few sine waves along the direction you pick - the dominant wave, a higher-frequency secondary one, and a diagonal term - mixing them by turbulence. Pixels get sampled from that shifted grid, so the image itself is never touched, just resampled. Two extra touches make it read as "heat":

  • edge_falloff ramps the effect toward the frame edges (hot air is strongest mid-frame, near a subject), and chroma_split_px displaces red and blue channels slightly differently, giving that faint chromatic shimmer real lenses produce.
  • direction (up/down/left/right) sets which way the ripple runs, and phase_deg animates it - wire a time-based value in for video and the waves creep forward frame to frame.

The strength_px value is your main knob: it's the displacement in pixels, and it's the one you'll tweak constantly. scale is the wave frequency - small numbers give big lazy waves, large numbers give tight boiling.

The inputs that matter

Like most of the pack's finishing nodes, the knobs live in one multiline settings_json string rather than a wall of widgets. The defaults are sane: strength_px 8.0, scale 3.2, turbulence 0.55, chroma_split_px 0.8, mix 1.0. The mask input is optional - wire a mask to restrict the shimmer to a region (say, just the area behind a car exhaust) and mask_feather softens the transition.

It outputs the shifted image, a mask (the effect coverage, useful if you want to chain or compare), and a heat_haze_info string that reports the resolved settings - read it with a Show Text node if you're debugging.

Installing it

Same story for every node in this pack:

cd ComfyUI/custom_nodes
git clone https://github.com/criskb/MKRShift_Nodes

then restart ComfyUI. Or search MKRShift_Nodes in ComfyUI Manager and let it do the clone. There are no pip requirements and no model downloads for the x1 VFX nodes - pure numpy/torch. (ffmpeg is only needed for the pack's video/audio export nodes, and OrcaSlicer only for the G-code ones; neither matters here.)

Gotchas

Keep strength_px modest - past a few percent it stops being shimmer and becomes a smeared mess. And remember the effect is strongest when the frame has fine detail to wobble; a soft, already-blurred background won't show much. If the node seems to do nothing, check that mix isn't 0 - at strength or mix of 0 it returns a passthrough and says so in the info string, which is the sort of graceful no-op you'll appreciate the third time you hit it.

CategoryMKRShift Nodes/VFX/Distortion

Inputs (3)

NameTypeDefaultDescription
imageIMAGE
settings_jsonSTRING{"direction":"up","strength_px":8.0,"scale":3.2,"phase_deg":0.0,"turbulence":0.55,"edge_falloff":0.35,"chroma_split_px":0.8,"mix":1.0,"mask_feather":6.0,"invert_mask":false}
maskoptMASK

Outputs (3)

NameTypeDescription
imageIMAGE
maskMASK
heat_haze_infoSTRING