Heat Haze
Cheap shimmer that sells a hot-plate look
- image
- mask
- image
- mask
- heat_haze_info
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_fallofframps the effect toward the frame edges (hot air is strongest mid-frame, near a subject), andchroma_split_pxdisplaces 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, andphase_deganimates 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.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| settings_json | STRING | {"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} | — |
| maskopt | MASK | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| mask | MASK | — |
| heat_haze_info | STRING | — |