Lens Breathing
The subtle refocus warp that adds life to video
- image
- depth_map
- mask
- image
- mask
- lens_breathing_info
Real lenses do something annoying-but-organic when you pull focus: the image visibly breathes. The magnification shifts slightly as the lens elements move, edges warp a hair, and color fringes a touch at the extremes. It's an optical flaw, and it's also one of the tiny tells that separate "rendered footage" from "shot footage." Lens Breathing is the MKRShift node that fakes that breathing warp on demand - the sort of subtle, deterministic lens flaw that sells a shot as captured rather than generated.
It's in MKRShift Nodes/VFX/Optics, the pack's lane for lens-flaw fakery (alongside Lens Distort, Lens Dirt Bloom, and Light Leak). The KB's photorealism doc makes the same point the node does: realism isn't one big trick, it's a stack of small optical truths - grain, aberration, vignette, and yes, breathing.
How it works
The node builds a radial displacement around center_x/center_y: pixels near the center barely move, pixels toward the edges get pushed by breath_amount, and edge_response controls how sharply that edge-weighted push ramps up. breath_amount can go negative, which flips it from an expanding warp to a contracting one - that's the "focus pulled in vs. out" feel. anisotropy stretches the warp horizontally or vertically for anamorphic flavor, and chroma adds a faint red/blue edge split so the breathing also fringes, like real lens breathing does.
The genuinely nice touch is the optional depth_map input. Real focus breathing is depth-related - the in-focus plane breathes differently from the out-of-focus background. Feed a depth map and the node modulates the warp by depth, so the background shimmers while the subject stays still. Wire the same depth map you'd use for any depth-aware effect in your graph.
Using it
breath_amount (default 0.08) is the master knob - this is a subtle effect, and at 0.08 it's already visible. For video, animate it: breathe it up for a push-in, down for a pull-back, or modulate it with an audio envelope for a music-driven rack. The settings bundle is otherwise small: edge_response, anisotropy, center_x/y, chroma, mix, mask_feather, invert_mask. Outputs are the warped image, a mask, and a lens_breathing_info string.
Installing it
cd ComfyUI/custom_nodes
git clone https://github.com/criskb/MKRShift_Nodes
then restart, or search MKRShift_Nodes in ComfyUI Manager. No models, no pip requirements.
The one mistake is overdoing it: at breath_amount above ~0.15 the edge warp becomes obvious distortion and your "subtle lens truth" turns into a fisheye wobble. If you can see the breathing without looking for it, back it off. And without a depth map the effect applies everywhere equally - fine for stylized use, but if the goal is realism, feed the depth. That single wire is the difference between "lens breathing" and "lens breathing done right."
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| settings_json | STRING | {"breath_amount":0.08,"edge_response":0.72,"anisotropy":0.0,"center_x":0.5,"center_y":0.5,"chroma":0.16,"mix":1.0,"mask_feather":12.0,"invert_mask":false} | — |
| depth_mapopt | MASK | — | |
| maskopt | MASK | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| mask | MASK | — |
| lens_breathing_info | STRING | — |