Depth Parallax Frames
Make your photos move — parallax frames with validity masks so the tears actually get fixed
- image
- depth_map
- frames
- validity_masks
- manifest_json
This is the "living photo" node - the one that takes a flat image plus its depth map and animates the camera through it so the foreground drifts past the background. The effect has been internet-bait since LeiaPix and DepthFlow made it effortless, and Depth Parallax Frames is the in-graph version from gokayfem's ComfyUI-Depth-Visualization pack. What makes it genuinely better than the weekend depth-warp scripts is what comes out the other side: real frame batches and validity masks that tell you exactly which pixels were invented by the warp, so you can inpaint the holes instead of shipping a video with smeared edges.
How it works
Each frame, the node moves a virtual camera along your chosen path - horizontal sway, vertical sway, ellipse (a nice lazy dolly-around), or dolly (push in and out, which scales about the frame center). Displacement per pixel comes from (depth - depth_center) * amplitude_px: pixels at depth_center stay put, everything closer or farther shifts by an amount proportional to how far they are from that pivot. So depth_center is your "what stays fixed" dial - set it where your subject sits. Higher amplitude_px means more drama and, inevitably, more tearing.
Warping is bilinear sampling with clamp, and here's the honest part: pixels that would sample from outside the source image don't exist. They get flagged 0 in the validity mask. The frames output is the pretty video; the validity_masks output is the truth about where it's faking.
Inputs and outputs that matter
imageanddepth_map- both in. Batches must match, or one of them can be a single image broadcast to the other's count.frames(24, 2–240) - how many frames per source image.amplitude_px(24) - total motion strength in pixels. Start here, not lower, and see the "burned" section.path- one of the four camera motions.loop(true) - seamless looping phases. Off if you want a one-way move.depth_center(0.5) - the depth that doesn't move.
Outputs:
frames(IMAGE batch) - feed to a video save node (VHS/VideoHelperSuite) or an AnimateDiff-style batch consumer.validity_masks(MASK batch) - 1 where sampled inside the source, 0 at exposed edges. This is the inpainting driver.manifest_json(STRING) - counts, path, amplitude, and a note on mask semantics.
Install
ComfyUI Manager, search ComfyUI-Depth-Visualization, install, restart. Manual:
cd ComfyUI/custom_nodes
git clone https://github.com/gokayfem/ComfyUI-Depth-Visualization.git
python -m pip install -r ComfyUI-Depth-Visualization/requirements.txt
Restart, node under depth/toolkit. Deps are numpy + Pillow; no models, fully offline, CPU-warp.
Where people get burned
- Amplitude is the tearing dial. The hard limit of parallax is structural: the depth map only describes the original camera view, and when the camera moves you reveal regions with no data. Push
amplitude_pxpast ~40–60 and the image starts tearing no matter how clean your input is. Keep the motion subtle, or budget for the fix below. - The fix is inpainting, and the masks are the map. Run the frames through an inpaint pass masked by
validity_masks(inverted) to fill exposed edges - the README's own framing is that these masks drive compositing/inpainting. This is the workflow that separates "demo clip" from "shippable video." - Un-normalized depth breaks
depth_center. If your depth isn't[0,1],depth_center0.5 points somewhere meaningless. Depth Normalize first, like every other node in this pack wants. - Wrong-sized batches error out. One image vs one depth is fine; mismatched multi-image batches raise a clear
ValueError. Match them or broadcast a single input.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| depth_map | IMAGE | — | |
| frames | INT | 242–240 | — |
| amplitude_px | FLOAT | 240–512 | — |
| path | COMBO | 4 options: horizontal sway, vertical sway, ellipse, dolly | |
| loop | BOOLEAN | true | — |
| depth_center | FLOAT | 0.500–1 | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| frames | IMAGE | — |
| validity_masks | MASK | — |
| manifest_json | STRING | — |