Video to Seamless Loop Converter
Upload a video clip to generate a seamless, looping video output.
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({
"158": {
"class_type": "VHS_LoadVideo",
"inputs": {
"custom_height": 0,
"custom_width": 0,
"force_rate": 16,
"format": "Wan",
"frame_load_cap": 0,
"select_every_nth": 1,
"skip_first_frames": 0,
"video": "UPLOADED_video_FILENAME"
},
"_meta": {
"title": "Load Video (Upload) 🎥🅥🅗🅢"
}
},
"176": {
"class_type": "ImageBatch",
"inputs": {
"image1": [
"229:160",
0
],
"image2": [
"177",
0
]
},
"_meta": {
"title": "Batch Images (DEPRECATED)"
}
},
"177": {
"class_type": "ImageFromBatch",
"inputs": {
"batch_index": [
"229:228",
0
],
"image": [
"225:8",
0
],
"length": [
"229:203",
0
]
},
"_meta": {
"title": "Get Image from Batch"
}
},
"250": {
"class_type": "CreateVideo",
"inputs": {
"audio": [
"158",
2
],
"bit_depth": "auto",
"codec": "none",
"color_space": "sRGB",
"fps": 16,
"images": [
"176",
0
]
},
"_meta": {
"title": "Create Video"
}
},
"251": {
"class_type": "SaveVideo",
"inputs": {
"filename_prefix": "video/ComfyUI",
"format": "auto",
"format.codec": "auto",
"video": [
"250",
0
]
},
"_meta": {
"title": "Save Video"
}
},
"225:159": {
"class_type": "ReverseImageBatch",
"inputs": {
"images": [
"229:160",
0
]
},
"_meta": {
"title": "Reverse Image Batch"
}
},
"225:162": {
"class_type": "ImageFromBatch",
"inputs": {
"batch_index": 0,
"image": [
"225:159",
0
],
"length": 1
},
"_meta": {
"title": "Get Image from Batch"
}
},
"225:182": {
"class_type": "OpenAIChatNode",
"inputs": {
"images": [
"225:162",
0
],
"model": "gpt-5-mini",
"persist_context": False,
"prompt": "Analyze the image closely and describe it with high visual fidelity.\n\nWrite a focused prompt targeting roughly 100 tokens. Use dense, specific, cinematic language with minimal fluff. Return exactly one paragraph, 1-2 sentences only. No titles, no labels, no bullet points, no numbering, no explanations, no quotation marks, no markdown, and no extra text before or after the prompt.\n\nOutput prompt only."
},
"_meta": {
"title": "OpenAI ChatGPT"
}
},
"225:183": {
"class_type": "UNETLoader",
"inputs": {
"unet_name": "wan2.1_vace_14B_fp16.safetensors",
"weight_dtype": "default"
},
"_meta": {
"title": "Load Diffusion Model"
}
},
"225:188": {
"class_type": "LoraLoaderModelOnly",
"inputs": {
"lora_name": "Wan21_T2V_14B_lightx2v_cfg_step_distill_lora_rank32.safetensors",
"model": [
"225:183",
0
],
"strength_model": 0.7
},
"_meta": {
"title": "Load LoRA"
}
},
"225:201": {
"class_type": "PreviewAny",
"inputs": {
"source": [
"225:182",
0
]
},
"_meta": {
"title": "Preview as Text"
}
},
"225:244": {
"class_type": "UNetTemporalAttentionMultiply",
"inputs": {
"cross_structural": 1.2,
"cross_temporal": 1.3,
"model": [
"225:188",
0
],
"self_structural": 1,
"self_temporal": 1
},
"_meta": {
"title": "UNetTemporalAttentionMultiply"
}
},
"225:3": {
"class_type": "KSampler",
"inputs": {
"cfg": 1,
"denoise": 1,
"latent_image": [
"225:83",
2
],
"model": [
"225:244",
0
],
"negative": [
"225:83",
1
],
"positive": [
"225:83",
0
],
"sampler_name": "euler",
"scheduler": "simple",
"seed": 276217540434315,
"steps": 8
},
"_meta": {
"title": "KSampler"
}
},
"225:38": {
"class_type": "CLIPLoader",
"inputs": {
"clip_name": "umt5_xxl_fp8_e4m3fn_scaled.safetensors",
"device": "default",
"type": "wan"
},
"_meta": {
"title": "Load CLIP"
}
},
"225:39": {
"class_type": "VAELoader",
"inputs": {
"vae_name": "wan_2.1_vae.safetensors"
},
"_meta": {
"title": "Load VAE"
}
},
"225:6": {
"class_type": "CLIPTextEncode",
"inputs": {
"clip": [
"225:38",
0
],
"text": [
"225:182",
0
]
},
"_meta": {
"title": "CLIP Text Encode (Positive Prompt)"
}
},
"225:7": {
"class_type": "CLIPTextEncode",
"inputs": {
"clip": [
"225:38",
0
],
"text": ""
},
"_meta": {
"title": "CLIP Text Encode (Negative Prompt)"
}
},
"225:8": {
"class_type": "VAEDecode",
"inputs": {
"samples": [
"225:3",
0
],
"vae": [
"225:39",
0
]
},
"_meta": {
"title": "VAE Decode"
}
},
"225:83": {
"class_type": "WanVaceToVideo",
"inputs": {
"batch_size": 1,
"control_masks": [
"229:217",
0
],
"control_video": [
"229:184",
0
],
"height": [
"229:160",
2
],
"length": [
"229:200",
0
],
"negative": [
"225:7",
0
],
"positive": [
"225:6",
0
],
"strength": 1,
"vae": [
"225:39",
0
],
"width": [
"229:160",
1
]
},
"_meta": {
"title": "WanVaceToVideo"
}
},
"229:144": {
"class_type": "VHS_DuplicateImages",
"inputs": {
"images": [
"229:197",
0
],
"multiply_by": [
"229:203",
0
]
},
"_meta": {
"title": "Repeat Images 🎥🅥🅗🅢"
}
},
"229:157": {
"class_type": "ImageFromBatch",
"inputs": {
"batch_index": 0,
"image": [
"229:160",
0
],
"length": [
"229:228",
0
]
},
"_meta": {
"title": "Get Image from Batch"
}
},
"229:160": {
"class_type": "ImageResize+",
"inputs": {
"condition": "always",
"height": 720,
"image": [
"158",
0
],
"interpolation": "lanczos",
"method": "keep proportion",
"multiple_of": 32,
"width": 720
},
"_meta": {
"title": "🔧 Image Resize"
}
},
"229:163": {
"class_type": "ImageFromBatch",
"inputs": {
"batch_index": 0,
"image": [
"229:236",
0
],
"length": [
"229:228",
0
]
},
"_meta": {
"title": "Get Image from Batch"
}
},
"229:184": {
"class_type": "BatchImagesNode",
"inputs": {
"images.image0": [
"229:237",
0
],
"images.image1": [
"229:144",
0
],
"images.image2": [
"229:157",
0
]
},
"_meta": {
"title": "Batch Images"
}
},
"229:197": {
"class_type": "LayerUtility: ColorImage V2",
"inputs": {
"color": "#7F7F7F",
"custom_height": [
"229:160",
2
],
"custom_width": [
"229:160",
1
],
"size": "custom"
},
"_meta": {
"title": "LayerUtility: ColorImage V2"
}
},
"229:200": {
"class_type": "PrimitiveInt",
"inputs": {
"value": 121
},
"_meta": {
"title": "Length"
}
},
"229:203": {
"class_type": "SimpleMath+",
"inputs": {
"a": [
"229:200",
0
],
"b": 0,
"value": "a - (16 * 2)"
},
"_meta": {
"title": "🔧 Simple Math"
}
},
"229:213": {
"class_type": "VHS_DuplicateImages",
"inputs": {
"images": [
"229:216",
0
],
"multiply_by": [
"229:203",
0
]
},
"_meta": {
"title": "Repeat Images 🎥🅥🅗🅢"
}
},
"229:214": {
"class_type": "BatchImagesNode",
"inputs": {
"images.image0": [
"229:224",
0
],
"images.image1": [
"229:213",
0
],
"images.image2": [
"229:224",
0
]
},
"_meta": {
"title": "Batch Images"
}
},
"229:216": {
"class_type": "LayerUtility: ColorImage V2",
"inputs": {
"color": "#ffffff",
"custom_height": [
"229:160",
2
],
"custom_width": [
"229:160",
1
],
"size": "custom"
},
"_meta": {
"title": "LayerUtility: ColorImage V2"
}
},
"229:217": {
"class_type": "ImageToMask",
"inputs": {
"channel": "red",
"image": [
"229:214",
0
]
},
"_meta": {
"title": "Convert Image to Mask"
}
},
"229:224": {
"class_type": "VHS_DuplicateImages",
"inputs": {
"images": [
"229:235",
0
],
"multiply_by": [
"229:228",
0
]
},
"_meta": {
"title": "Repeat Images 🎥🅥🅗🅢"
}
},
"229:228": {
"class_type": "PrimitiveInt",
"inputs": {
"value": 16
},
"_meta": {
"title": "Length"
}
},
"229:235": {
"class_type": "LayerUtility: ColorImage V2",
"inputs": {
"color": "#000000",
"custom_height": [
"229:160",
2
],
"custom_width": [
"229:160",
1
],
"size": "custom"
},
"_meta": {
"title": "LayerUtility: ColorImage V2"
}
},
"229:236": {
"class_type": "ReverseImageBatch",
"inputs": {
"images": [
"229:160",
0
]
},
"_meta": {
"title": "Reverse Image Batch"
}
},
"229:237": {
"class_type": "ReverseImageBatch",
"inputs": {
"images": [
"229:163",
0
]
},
"_meta": {
"title": "Reverse Image Batch"
}
}
})
workflow.set_input("158", "video", client.assets.from_url("https://comfy.org/catalog-media/34cf531f-9d9b-496e-b151-9d9eb1884ad4/6e9246b2-b8a7-468a-9bdc-ff766d66abf5"))
# 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("251"):
output.to_file(output.name)Try an example
About this workflow
The 'Video to Seamless Loop Converter' workflow is designed to transform your video clips into seamless, looping videos. This is particularly useful for creating engaging content for social media, digital art installations, or any application where continuous playback is desired. The workflow leverages the Wan2.1 VACE model, which excels in video content analysis and enhancement, ensuring that the transition between the end and the start of the video is smooth and imperceptible.
Technically, the workflow begins with the VHS_LoadVideo node, which imports the video into the system. The video frames are processed using a series of nodes, including ImageBatch and ImageFromBatch, which manage the frame data efficiently. The custom nodes from comfyui-kjnodes and comfyui-videohelpersuite are employed to create and fill empty frames, ensuring a seamless transition. Finally, the CreateVideo and SaveVideo nodes compile the processed frames into a continuous loop, ready for export and use.
Questions & answers
The workflow supports common video formats such as MP4, AVI, and MOV. Ensure your video is in one of these formats for optimal results.
The loop length is flexible and can be set according to your needs. However, shorter loops may require more precise frame alignment for seamless transitions.
This workflow is designed for pre-recorded video clips. For live feeds, additional real-time processing capabilities would be required.
The workflow aims to maintain the original video quality while ensuring a seamless loop. However, some minor quality adjustments may occur during processing.