LTX Cleanplate for VFX
Prompt an object, and SAM3 + LTX 2.3 mask-conditioned inpainting remove it and rebuild the clean background plate."
Inside the workflow
A read-only preview of the source template. Open it in Cloud to explore the nodes and make changes.
Loading the graph
Open full-size workflow preview- Output
- Video
- Author
- ComfyUI
Call this model from your code
The request below uses the same prepared graph and settings as the Playground. Each submission can start a paid run; do not automatically retry a submission whose outcome is unknown.
What you need
- Runs on
- Comfy Cloud
- Key
- COMFY_API_KEY
- Your files
- Uploaded by the code from their urls
Needs a paid Cloud plan and credits.
# Python 3.10+: pip install comfy-sdk==0.4.0
import os
from comfy_sdk import Comfy
api_key = os.environ.get("COMFY_API_KEY", "").strip()
if not api_key: raise RuntimeError("Set COMFY_API_KEY")
os.environ.setdefault("COMFY_BASE_URL", "https://cloud.comfy.org")
client = Comfy(api_key=api_key)
workflow = client.workflows.from_json({
"52": {
"class_type": "SaveVideo",
"inputs": {
"codec": "auto",
"filename_prefix": "cleanplate_compare",
"format": "auto",
"format.codec": "auto",
"video": [
"54:31",
0
]
},
"_meta": {
"title": "Save Video Compared"
}
},
"53": {
"class_type": "SaveVideo",
"inputs": {
"codec": "auto",
"filename_prefix": "cleanplate_ltx_inpaint",
"format": "auto",
"format.codec": "auto",
"video": [
"54:36",
0
]
},
"_meta": {
"title": "Save Video"
}
},
"58": {
"class_type": "GetVideoComponents",
"inputs": {
"video": [
"117",
0
]
},
"_meta": {
"title": "Plate frames"
}
},
"59": {
"class_type": "GetVideoComponents",
"inputs": {
"video": [
"54:36",
0
]
},
"_meta": {
"title": "LTX inpaint frames"
}
},
"63": {
"class_type": "VHS_GetImageCount",
"inputs": {
"images": [
"58",
0
]
},
"_meta": {
"title": "n"
}
},
"64": {
"class_type": "ComfyMathExpression",
"inputs": {
"expression": "(int((a - 1) / 8)) * 8 + 1",
"values.a": [
"63",
0
]
},
"_meta": {
"title": "L = valid LTX length"
}
},
"65": {
"class_type": "GetImageRangeFromBatch",
"inputs": {
"images": [
"58",
0
],
"num_frames": [
"64",
1
],
"start_index": 0
},
"_meta": {
"title": "Trim plate to L"
}
},
"66": {
"class_type": "LTXVDilateVideoMask",
"inputs": {
"mask": [
"73",
0
],
"spatial_radius": 10,
"temporal_radius": 1
},
"_meta": {
"title": "Dilate matte"
}
},
"67": {
"class_type": "GrowMaskWithBlur",
"inputs": {
"blur_radius": 4,
"decay_factor": 1,
"expand": 0,
"fill_holes": False,
"flip_input": False,
"incremental_expandrate": 0,
"lerp_alpha": 1,
"mask": [
"66",
0
],
"tapered_corners": True
},
"_meta": {
"title": "Feather"
}
},
"68": {
"class_type": "LTXVInpaintPreprocess",
"inputs": {
"images": [
"65",
0
],
"mask": [
"67",
0
]
},
"_meta": {
"title": "Green-fill masked region"
}
},
"69": {
"class_type": "CreateVideo",
"inputs": {
"bit_depth": 8,
"codec": "none",
"color_space": "sRGB",
"fps": [
"58",
2
],
"images": [
"68",
0
]
},
"_meta": {
"title": "-> subgraph guide"
}
},
"70": {
"class_type": "LoadSAM3Model",
"inputs": {
"model_path": "models/sam3/sam3.pt"
},
"_meta": {
"title": "(down)Load SAM3 Model"
}
},
"71": {
"class_type": "SAM3VideoSegmentation",
"inputs": {
"frame_idx": 0,
"prompt_mode": "text",
"score_threshold": 0.3,
"text_prompt": [
"130",
0
],
"video_frames": [
"65",
0
]
},
"_meta": {
"title": "SAM3 (object prompt)"
}
},
"72": {
"class_type": "SAM3Propagate",
"inputs": {
"direction": "both",
"end_frame": -1,
"offload_model": False,
"sam3_model": [
"70",
0
],
"start_frame": 0,
"video_state": [
"71",
0
]
},
"_meta": {
"title": "SAM3 Propagate"
}
},
"73": {
"class_type": "SAM3VideoOutput",
"inputs": {
"masks": [
"72",
0
],
"obj_id": -1,
"plot_all_masks": True,
"scores": [
"72",
1
],
"video_state": [
"72",
2
]
},
"_meta": {
"title": "SAM3 Video Output"
}
},
"75": {
"class_type": "MaskToImage",
"inputs": {
"mask": [
"67",
0
]
},
"_meta": {
"title": "Convert Mask to Image"
}
},
"76": {
"class_type": "CreateVideo",
"inputs": {
"bit_depth": 8,
"codec": "none",
"color_space": "sRGB",
"fps": [
"58",
2
],
"images": [
"75",
0
]
},
"_meta": {
"title": "Create Video"
}
},
"77": {
"class_type": "SaveVideo",
"inputs": {
"codec": "auto",
"filename_prefix": "cleanplate_matte",
"format": "auto",
"format.codec": "auto",
"video": [
"76",
0
]
},
"_meta": {
"title": "Save Matte (Nuke)"
}
},
"116": {
"class_type": "VHS_LoadVideo",
"inputs": {
"custom_height": 0,
"custom_width": 0,
"force_rate": 0,
"format": "LTXV",
"frame_load_cap": 0,
"select_every_nth": 1,
"skip_first_frames": 0,
"video": "UPLOADED_video_FILENAME"
},
"_meta": {
"title": "Load Video (Upload) 🎥🅥🅗🅢"
}
},
"117": {
"class_type": "CreateVideo",
"inputs": {
"audio": [
"116",
2
],
"bit_depth": 8,
"codec": "none",
"color_space": "sRGB",
"fps": [
"118",
0
],
"images": [
"116",
0
]
},
"_meta": {
"title": "Create Video"
}
},
"118": {
"class_type": "VHS_VideoInfo",
"inputs": {
"video_info": [
"116",
3
]
},
"_meta": {
"title": "Video Info 🎥🅥🅗🅢"
}
},
"130": {
"class_type": "PrimitiveString",
"inputs": {
"value": "cart"
},
"_meta": {
"title": "What do you want to remove?"
}
},
"133": {
"class_type": "PreviewAny",
"inputs": {
"source": [
"134",
0
]
},
"_meta": {
"title": "Final prompt"
}
},
"134": {
"class_type": "StringConcatenate",
"inputs": {
"delimiter": " ",
"string_a": [
"135",
0
],
"string_b": "removed"
},
"_meta": {
"title": "+ suffix"
}
},
"135": {
"class_type": "StringConcatenate",
"inputs": {
"delimiter": " ",
"string_a": "background plate, the scene with the",
"string_b": [
"130",
0
]
},
"_meta": {
"title": "prefix + object"
}
},
"54:1": {
"class_type": "VAELoaderKJ",
"inputs": {
"device": "main_device",
"vae_name": "LTX23_video_vae_bf16.safetensors",
"weight_dtype": "bf16"
},
"_meta": {
"title": "VAELoader KJ"
}
},
"54:10": {
"class_type": "EmptyLTXVLatentVideo",
"inputs": {
"batch_size": 1,
"height": [
"54:6",
1
],
"length": [
"54:45",
1
],
"width": [
"54:6",
0
]
},
"_meta": {
"title": "Empty LTXV Latent Video"
}
},
"54:11": {
"class_type": "LTXVConditioning",
"inputs": {
"frame_rate": [
"54:49",
0
],
"negative": [
"54:22",
0
],
"positive": [
"54:9",
0
]
},
"_meta": {
"title": "LTXVConditioning"
}
},
"54:12": {
"class_type": "LTXAddVideoICLoRAGuide",
"inputs": {
"crop": "disabled",
"frame_idx": 0,
"image": [
"54:41",
0
],
"latent": [
"54:10",
0
],
"latent_downscale_factor": 1,
"negative": [
"54:11",
1
],
"positive": [
"54:11",
0
],
"strength": 1,
"tile_frames": 97,
"tile_overlap": 64,
"tile_size": 256,
"use_streaming": False,
"use_tiled_encode": False,
"vae": [
"54:1",
0
]
},
"_meta": {
"title": "🅛🅣🅧 Add Video IC-LoRA Guide"
}
},
"54:126": {
"class_type": "ComfyNumberConvert",
"inputs": {
"value": [
"58",
2
]
},
"_meta": {
"title": "Convert Number"
}
},
"54:13": {
"class_type": "LTXVAudioVAEDecode",
"inputs": {
"audio_vae": [
"54:2",
0
],
"samples": [
"54:18",
1
]
},
"_meta": {
"title": "LTXV Audio VAE Decode"
}
},
"54:14": {
"class_type": "LoraLoaderModelOnly",
"inputs": {
"lora_name": "ltx-2.3-22b-distilled-lora-384-1.1.safetensors",
"model": [
"54:15",
0
],
"strength_model": 0.4
},
"_meta": {
"title": "Load LoRA"
}
},
"54:15": {
"class_type": "LoraLoaderModelOnly",
"inputs": {
"lora_name": "ltx-2.3-22b-ic-lora-in-outpainting-0.9.safetensors",
"model": [
"54:5",
0
],
"strength_model": 1
},
"_meta": {
"title": "Load LoRA"
}
},
"54:16": {
"class_type": "VAEDecode",
"inputs": {
"samples": [
"54:17",
2
],
"vae": [
"54:1",
0
]
},
"_meta": {
"title": "VAE Decode"
}
},
"54:17": {
"class_type": "LTXVCropGuides",
"inputs": {
"latent": [
"54:18",
0
],
"negative": [
"54:12",
1
],
"positive": [
"54:12",
0
]
},
"_meta": {
"title": "LTXVCropGuides"
}
},
"54:18": {
"class_type": "LTXVSeparateAVLatent",
"inputs": {
"av_latent": [
"54:19",
0
]
},
"_meta": {
"title": "Separate AV Latent"
}
},
"54:19": {
"class_type": "KSampler",
"inputs": {
"cfg": 1,
"denoise": 1,
"latent_image": [
"54:20",
0
],
"model": [
"54:14",
0
],
"negative": [
"54:12",
1
],
"positive": [
"54:12",
0
],
"sampler_name": "euler_ancestral_cfg_pp",
"scheduler": "linear_quadratic",
"seed": 511332212712579,
"steps": 8
},
"_meta": {
"title": "KSampler"
}
},
"54:2": {
"class_type": "VAELoaderKJ",
"inputs": {
"device": "main_device",
"vae_name": "LTX23_audio_vae_bf16.safetensors",
"weight_dtype": "bf16"
},
"_meta": {
"title": "VAELoader KJ"
}
},
"54:20": {
"class_type": "LTXVConcatAVLatent",
"inputs": {
"audio_latent": [
"54:21",
0
],
"video_latent": [
"54:12",
2
]
},
"_meta": {
"title": "Concat AV Latent"
}
},
"54:21": {
"class_type": "LTXVEmptyLatentAudio",
"inputs": {
"audio_vae": [
"54:2",
0
],
"batch_size": 1,
"frame_rate": [
"54:50",
0
],
"frames_number": [
"54:45",
1
]
},
"_meta": {
"title": "Empty LTXV Latent Audio"
}
},
"54:22": {
"class_type": "CLIPTextEncode",
"inputs": {
"clip": [
"54:4",
0
],
"text": "blurry, low resolution, ugly, garbled mess, compression artifacts, glitch, stop motion, still image, pixelated, robotic voice, oversaturated"
},
"_meta": {
"title": "CLIP Text Encode (Prompt)"
}
},
"54:25": {
"class_type": "ImageFromBatch",
"inputs": {
"batch_index": 0,
"image": [
"54:41",
0
],
"length": 1
},
"_meta": {
"title": "Get Image from Batch"
}
},
"54:26": {
"class_type": "ImageStitch",
"inputs": {
"direction": "down",
"image1": [
"54:41",
0
],
"image2": [
"54:16",
0
],
"match_image_size": True,
"spacing_color": "white",
"spacing_width": 0
},
"_meta": {
"title": "Stitch Images"
}
},
"54:3": {
"class_type": "VHS_GetImageCount",
"inputs": {
"images": [
"54:38",
0
]
},
"_meta": {
"title": "Get Image Count 🎥🅥🅗🅢"
}
},
"54:31": {
"class_type": "CreateVideo",
"inputs": {
"bit_depth": 8,
"codec": "none",
"color_space": "sRGB",
"fps": [
"54:49",
0
],
"images": [
"54:26",
0
]
},
"_meta": {
"title": "Create Video"
}
},
"54:36": {
"class_type": "CreateVideo",
"inputs": {
"audio": [
"54:38",
1
],
"bit_depth": 8,
"codec": "none",
"color_space": "sRGB",
"fps": [
"54:49",
0
],
"images": [
"54:16",
0
]
},
"_meta": {
"title": "Create Video"
}
},
"54:38": {
"class_type": "GetVideoComponents",
"inputs": {
"video": [
"69",
0
]
},
"_meta": {
"title": "Get Video Components"
}
},
"54:4": {
"class_type": "DualCLIPLoader",
"inputs": {
"clip_name1": "gemma_3_12B_it_fp4_mixed.safetensors",
"clip_name2": "ltx-2.3_text_projection_bf16.safetensors",
"device": "default",
"type": "ltxv"
},
"_meta": {
"title": "Load CLIP (Dual)"
}
},
"54:41": {
"class_type": "ResizeImageMaskNode",
"inputs": {
"input": [
"54:38",
0
],
"resize_type": "scale dimensions",
"resize_type.crop": "disabled",
"resize_type.height": [
"118",
4
],
"resize_type.width": [
"118",
3
],
"scale_method": "area"
},
"_meta": {
"title": "Resize Image/Mask (s1 size)"
}
},
"54:43": {
"class_type": "PreviewAny",
"inputs": {
"source": [
"54:45",
1
]
},
"_meta": {
"title": "Preview as Text"
}
},
"54:45": {
"class_type": "ComfyMathExpression",
"inputs": {
"expression": "(int((a - 1) / 8)) * 8 + 1",
"values.a": [
"54:3",
0
]
},
"_meta": {
"title": "Math Expression"
}
},
"54:49": {
"class_type": "ComfyNumberConvert",
"inputs": {
"value": [
"54:50",
0
]
},
"_meta": {
"title": "Convert Number"
}
},
"54:5": {
"class_type": "UNETLoader",
"inputs": {
"unet_name": "ltx-2.3-22b-dev_transformer_only_bf16.safetensors",
"weight_dtype": "default"
},
"_meta": {
"title": "Load Diffusion Model"
}
},
"54:50": {
"class_type": "PrimitiveInt",
"inputs": {
"value": [
"54:126",
1
]
},
"_meta": {
"title": "fps"
}
},
"54:6": {
"class_type": "GetImageSize",
"inputs": {
"image": [
"54:25",
0
]
},
"_meta": {
"title": "Get Image Size"
}
},
"54:9": {
"class_type": "CLIPTextEncode",
"inputs": {
"clip": [
"54:4",
0
],
"text": [
"133",
0
]
},
"_meta": {
"title": "CLIP Text Encode (Prompt)"
}
}
})
workflow.set_input("116", "video", client.assets.from_url("https://comfy.org/catalog-media/9c45ce1c-1805-4be7-ac67-11f7bb6d02fa/40969461-d6a9-41e2-9e20-eb8d27b27314"))
# run() also passes api_key to the partner nodes in this workflow.
job = client.run(workflow, api_key=api_key)
for output in job.get_outputs("53"):
output.to_file(output.name)
for output in job.get_outputs("52"):
output.to_file(output.name)
for output in job.get_outputs("77"):
output.to_file(output.name)Try an example
About this workflow
This ComfyUI workflow builds a clean background plate by removing an object from video using SAM3-driven masks and a mask-conditioned LTX-2.3 In‑Outpainting IC‑LoRA. The pipeline loads your clip with VHS_LoadVideo/VHS_VideoInfo, extracts frames via GetVideoComponents, and uses LoadSAM3Model → SAM3VideoSegmentation to seed a mask for the object you want to remove. SAM3Propagate tracks that mask across the shot, and SAM3VideoOutput provides a per‑frame OBJECT_MASK. To ensure seamless fills, LTXVDilateVideoMask expands the mask (e.g., s10/t1) and GrowMaskWithBlur feathers the edges.
The masked region is then green‑filled by LTXVInpaintPreprocess, and CreateVideo re‑encodes those preprocessed frames. That green plate, together with the OBJECT_MASK and your minimal background‑only prompt, feeds the LTX 2.3 In‑Outpainting IC‑LoRA (packaged here as a subgraph node), which is explicitly mask‑conditioned to generate only inside the masked area. Utility nodes like VHS_GetImageCount, ComfyMathExpression, and GetImageRangeFromBatch let you preview limited frame ranges before a full run. Finally, SaveVideo writes the cleanplate result for downstream compositing.
Questions & answers
Keep it empty or describe only the background inside the masked area (e.g., “asphalt road with painted lines, soft shadow”). Avoid mentioning the removed object or unrelated foreground items—this can cause duplication or hallucinations.
Increase LTXVDilateVideoMask size to cover motion blur and expand beyond the object, then raise GrowMaskWithBlur feather radius to soften transitions. Also re‑seed SAM3 on a clearer frame and re‑propagate if the tracked mask drifts.
Yes. Use VHS_GetImageCount to see the total frames and GetImageRangeFromBatch (optionally with ComfyMathExpression) to select a small frame window for quick iterations, then switch back to the full range once satisfied.
SaveVideo typically outputs a silent file. If you need audio, re‑mux the original track in your NLE or a tool like ffmpeg. If your setup routes audio via GetVideoComponents, you can also handle pass‑through externally after render.