Looped Restyler
Upload a starting image and its prompt to generate a looping video from the first to the last frame.
概要
The 'Looped Restyler' workflow in ComfyUI is designed to transform a static image into a dynamic looping video. By utilizing advanced AI models like Nano Banana, Google Gemini, and Wan2.2, this workflow seamlessly transitions from the first frame of an image to the last, creating a continuous loop.
The process begins with loading an initial image and its corresponding prompt, which guides the transformation process. The workflow employs nodes such as LoadImage to import the starting image, and SaveVideo to compile the frames into a video format. The node identified by 3313ce14-acfd-4a9c-9f0c-a1b01147e934 plays a crucial role in generating intermediate frames that ensure a smooth transition between the start and end of the sequence. PreviewImage is used to monitor the progress and make necessary adjustments before finalizing the output.
入力するもの
- ワークフローを読み込み中
ComfyUI で実行できます。 このワークフローはこのページでは実行できません。すべてが読み込まれた状態でComfy Cloudで開くか、ご自身のComfyUI用にダウンロードしてください。
Cloudで試すワークフローの内部
ノードグラフの読み取り専用プレビューです。ノードを編集するには、Comfy Cloudで開くかダウンロードしてください。
Loading the graph
Open full-size workflow previewカスタムノードが必要です
- ワークフローを読み込み中
コードからこのワークフローを使用する
可能な場合はワークフローグラフと入力から生成されます。それ以外の場合は保存された例が表示されます。料金が発生する場合があるため、コードを実行する前に認証情報とエンドポイントを確認してください。
What you need
- Runs on
- Comfy Cloud
- キー
- COMFY_API_KEY
有料の Cloud プランとクレジットが必要です。
# 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({
"4": {
"class_type": "LoadImage",
"inputs": {
"image": "looped_restyler_init.png"
},
"_meta": {
"title": "Load Image"
}
},
"80": {
"class_type": "SaveVideo",
"inputs": {
"filename_prefix": "video/ComfyUI",
"format": "auto",
"format.codec": "auto",
"video": [
"89:79",
0
]
},
"_meta": {
"title": "Save Video"
}
},
"90": {
"class_type": "PreviewImage",
"inputs": {
"images": [
"89:82",
0
]
},
"_meta": {
"title": "Preview Image"
}
},
"89:27": {
"class_type": "SplitImageWithAlpha",
"inputs": {
"image": [
"89:88",
0
]
},
"_meta": {
"title": "Split Image with Alpha"
}
},
"89:5": {
"class_type": "GeminiImageNode",
"inputs": {
"aspect_ratio": "auto",
"images": [
"89:87",
0
],
"model": "gemini-2.5-flash-image",
"prompt": "A pure illustrated anime image. \n\nThe camera angle is from the back of the character, a back-facing shot.\n\n2D line-art with cel-shaded color fills, expressive outlines, and stylized anime anatomy. Scene is drawn entirely as an illustration. Absolutely no realism, no photo textures, no 3D render qualities — strictly anime illustration style.",
"response_modalities": "IMAGE+TEXT",
"seed": 754210146642470,
"system_prompt": "You are an expert image-generation engine. You must ALWAYS produce an image.\nInterpret all user input—regardless of format, intent, or abstraction—as literal visual directives for image composition.\nIf a prompt is conversational or lacks specific visual details, you must creatively invent a concrete visual scenario that depicts the concept.\nPrioritize generating the visual representation above any text, formatting, or conversational requests."
},
"_meta": {
"title": "Nano Banana (Google Gemini Image)"
}
},
"89:6": {
"class_type": "PreviewImage",
"inputs": {
"images": [
"89:88",
0
]
},
"_meta": {
"title": "Preview Image"
}
},
"89:73:57": {
"class_type": "CLIPTextEncode",
"inputs": {
"clip": [
"89:73:69",
0
],
"text": "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走"
},
"_meta": {
"title": "CLIP Text Encode (Negative Prompt)"
}
},
"89:73:58": {
"class_type": "CLIPTextEncode",
"inputs": {
"clip": [
"89:73:69",
0
],
"text": [
"89:81",
0
]
},
"_meta": {
"title": "CLIP Text Encode (Positive Prompt)"
}
},
"89:73:59": {
"class_type": "UNETLoader",
"inputs": {
"unet_name": "wan2.2_i2v_high_noise_14B_fp8_scaled.safetensors",
"weight_dtype": "default"
},
"_meta": {
"title": "Load Diffusion Model"
}
},
"89:73:60": {
"class_type": "UNETLoader",
"inputs": {
"unet_name": "wan2.2_i2v_low_noise_14B_fp8_scaled.safetensors",
"weight_dtype": "default"
},
"_meta": {
"title": "Load Diffusion Model"
}
},
"89:73:61": {
"class_type": "LoraLoaderModelOnly",
"inputs": {
"lora_name": "wan2.2_i2v_lightx2v_4steps_lora_v1_low_noise.safetensors",
"model": [
"89:73:60",
0
],
"strength_model": 1
},
"_meta": {
"title": "Load LoRA"
}
},
"89:73:62": {
"class_type": "LoraLoaderModelOnly",
"inputs": {
"lora_name": "wan2.2_i2v_lightx2v_4steps_lora_v1_high_noise.safetensors",
"model": [
"89:73:59",
0
],
"strength_model": 1
},
"_meta": {
"title": "Load LoRA"
}
},
"89:73:63": {
"class_type": "ModelSamplingSD3",
"inputs": {
"model": [
"89:73:62",
0
],
"shift": 5
},
"_meta": {
"title": "ModelSamplingSD3"
}
},
"89:73:64": {
"class_type": "ModelSamplingSD3",
"inputs": {
"model": [
"89:73:61",
0
],
"shift": 5
},
"_meta": {
"title": "ModelSamplingSD3"
}
},
"89:73:65": {
"class_type": "CreateVideo",
"inputs": {
"bit_depth": "auto",
"codec": "none",
"color_space": "sRGB",
"fps": 24,
"images": [
"89:73:71",
0
]
},
"_meta": {
"title": "Create Video"
}
},
"89:73:66": {
"class_type": "KSamplerAdvanced",
"inputs": {
"add_noise": "enable",
"cfg": 1,
"end_at_step": 2,
"latent_image": [
"89:73:67",
2
],
"model": [
"89:73:63",
0
],
"negative": [
"89:73:67",
1
],
"noise_seed": 803169991734672,
"positive": [
"89:73:67",
0
],
"return_with_leftover_noise": "enable",
"sampler_name": "euler",
"scheduler": "simple",
"start_at_step": 0,
"steps": 4
},
"_meta": {
"title": "KSampler (Advanced)"
}
},
"89:73:67": {
"class_type": "WanFirstLastFrameToVideo",
"inputs": {
"batch_size": 1,
"end_image": [
"89:27",
0
],
"height": [
"89:87",
2
],
"length": 81,
"negative": [
"89:73:57",
0
],
"positive": [
"89:73:58",
0
],
"start_image": [
"89:87",
0
],
"vae": [
"89:73:70",
0
],
"width": [
"89:87",
1
]
},
"_meta": {
"title": "WanFirstLastFrameToVideo"
}
},
"89:73:68": {
"class_type": "KSamplerAdvanced",
"inputs": {
"add_noise": "disable",
"cfg": 1,
"end_at_step": 10000,
"latent_image": [
"89:73:66",
0
],
"model": [
"89:73:64",
0
],
"negative": [
"89:73:67",
1
],
"noise_seed": 0,
"positive": [
"89:73:67",
0
],
"return_with_leftover_noise": "disable",
"sampler_name": "euler",
"scheduler": "simple",
"start_at_step": 2,
"steps": 4
},
"_meta": {
"title": "KSampler (Advanced)"
}
},
"89:73:69": {
"class_type": "CLIPLoader",
"inputs": {
"clip_name": "umt5_xxl_fp8_e4m3fn_scaled.safetensors",
"device": "default",
"type": "wan"
},
"_meta": {
"title": "Load CLIP"
}
},
"89:73:70": {
"class_type": "VAELoader",
"inputs": {
"vae_name": "wan_2.1_vae.safetensors"
},
"_meta": {
"title": "Load VAE"
}
},
"89:73:71": {
"class_type": "VAEDecode",
"inputs": {
"samples": [
"89:73:68",
0
],
"vae": [
"89:73:70",
0
]
},
"_meta": {
"title": "VAE Decode"
}
},
"89:76": {
"class_type": "GetVideoComponents",
"inputs": {
"video": [
"89:73:65",
0
]
},
"_meta": {
"title": "Get Video Components"
}
},
"89:77": {
"class_type": "ImageBatch",
"inputs": {
"image1": [
"89:76",
0
],
"image2": [
"89:78",
0
]
},
"_meta": {
"title": "Batch Images (DEPRECATED)"
}
},
"89:78": {
"class_type": "GetVideoComponents",
"inputs": {
"video": [
"89:91:56",
0
]
},
"_meta": {
"title": "Get Video Components"
}
},
"89:79": {
"class_type": "CreateVideo",
"inputs": {
"bit_depth": "auto",
"codec": "none",
"color_space": "sRGB",
"fps": 24,
"images": [
"89:77",
0
]
},
"_meta": {
"title": "Create Video"
}
},
"89:81": {
"class_type": "GeminiNode",
"inputs": {
"images": [
"89:82",
0
],
"model": "gemini-2.5-pro",
"prompt": "I am prompting a video model that understands natural language.\n\nI need a prompt that describes the transitions from image on the left to image on the right.\n\nJust give me the prompt and nothing else.",
"seed": 941151948383059,
"system_prompt": ""
},
"_meta": {
"title": "Google Gemini"
}
},
"89:82": {
"class_type": "ImageStitch",
"inputs": {
"direction": "right",
"image1": [
"89:87",
0
],
"image2": [
"89:88",
0
],
"match_image_size": True,
"spacing_color": "white",
"spacing_width": 0
},
"_meta": {
"title": "Stitch Images"
}
},
"89:83": {
"class_type": "GeminiNode",
"inputs": {
"images": [
"89:82",
0
],
"model": "gemini-2.5-pro",
"prompt": "I am prompting a video model that understands natural language.\n\nI need a prompt that describes the transitions from image on the right to image on the left.\n\nJust give me the prompt and nothing else.",
"seed": 23952267132674,
"system_prompt": ""
},
"_meta": {
"title": "Google Gemini"
}
},
"89:84": {
"class_type": "PreviewImage",
"inputs": {
"images": [
"89:82",
0
]
},
"_meta": {
"title": "Preview Image"
}
},
"89:85": {
"class_type": "ImageScaleToTotalPixels",
"inputs": {
"image": [
"4",
0
],
"megapixels": 1,
"resolution_steps": 1,
"upscale_method": "lanczos"
},
"_meta": {
"title": "Scale Image to Total Pixels"
}
},
"89:87": {
"class_type": "GetImageSizeAndCount",
"inputs": {
"image": [
"89:85",
0
]
},
"_meta": {
"title": "Get Image Size & Count"
}
},
"89:88": {
"class_type": "ImageScaleToTotalPixels",
"inputs": {
"image": [
"89:5",
0
],
"megapixels": 1,
"resolution_steps": 1,
"upscale_method": "lanczos"
},
"_meta": {
"title": "Scale Image to Total Pixels"
}
},
"89:91:29": {
"class_type": "CLIPTextEncode",
"inputs": {
"clip": [
"89:91:53",
0
],
"text": "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走"
},
"_meta": {
"title": "CLIP Text Encode (Negative Prompt)"
}
},
"89:91:33": {
"class_type": "VAEDecode",
"inputs": {
"samples": [
"89:91:42",
0
],
"vae": [
"89:91:54",
0
]
},
"_meta": {
"title": "VAE Decode"
}
},
"89:91:38": {
"class_type": "KSamplerAdvanced",
"inputs": {
"add_noise": "enable",
"cfg": 1,
"end_at_step": 2,
"latent_image": [
"89:91:40",
2
],
"model": [
"89:91:51",
0
],
"negative": [
"89:91:40",
1
],
"noise_seed": 757394574321365,
"positive": [
"89:91:40",
0
],
"return_with_leftover_noise": "enable",
"sampler_name": "euler",
"scheduler": "simple",
"start_at_step": 0,
"steps": 4
},
"_meta": {
"title": "KSampler (Advanced)"
}
},
"89:91:40": {
"class_type": "WanFirstLastFrameToVideo",
"inputs": {
"batch_size": 1,
"end_image": [
"89:87",
0
],
"height": [
"89:87",
2
],
"length": 81,
"negative": [
"89:91:29",
0
],
"positive": [
"89:91:41",
0
],
"start_image": [
"89:27",
0
],
"vae": [
"89:91:54",
0
],
"width": [
"89:87",
1
]
},
"_meta": {
"title": "WanFirstLastFrameToVideo"
}
},
"89:91:41": {
"class_type": "CLIPTextEncode",
"inputs": {
"clip": [
"89:91:53",
0
],
"text": [
"89:83",
0
]
},
"_meta": {
"title": "CLIP Text Encode (Positive Prompt)"
}
},
"89:91:42": {
"class_type": "KSamplerAdvanced",
"inputs": {
"add_noise": "disable",
"cfg": 1,
"end_at_step": 10000,
"latent_image": [
"89:91:38",
0
],
"model": [
"89:91:52",
0
],
"negative": [
"89:91:40",
1
],
"noise_seed": 0,
"positive": [
"89:91:40",
0
],
"return_with_leftover_noise": "disable",
"sampler_name": "euler",
"scheduler": "simple",
"start_at_step": 2,
"steps": 4
},
"_meta": {
"title": "KSampler (Advanced)"
}
},
"89:91:47": {
"class_type": "UNETLoader",
"inputs": {
"unet_name": "wan2.2_i2v_high_noise_14B_fp8_scaled.safetensors",
"weight_dtype": "default"
},
"_meta": {
"title": "Load Diffusion Model"
}
},
"89:91:48": {
"class_type": "UNETLoader",
"inputs": {
"unet_name": "wan2.2_i2v_low_noise_14B_fp8_scaled.safetensors",
"weight_dtype": "default"
},
"_meta": {
"title": "Load Diffusion Model"
}
},
"89:91:49": {
"class_type": "LoraLoaderModelOnly",
"inputs": {
"lora_name": "wan2.2_i2v_lightx2v_4steps_lora_v1_low_noise.safetensors",
"model": [
"89:91:48",
0
],
"strength_model": 1
},
"_meta": {
"title": "Load LoRA"
}
},
"89:91:50": {
"class_type": "LoraLoaderModelOnly",
"inputs": {
"lora_name": "wan2.2_i2v_lightx2v_4steps_lora_v1_high_noise.safetensors",
"model": [
"89:91:47",
0
],
"strength_model": 1
},
"_meta": {
"title": "Load LoRA"
}
},
"89:91:51": {
"class_type": "ModelSamplingSD3",
"inputs": {
"model": [
"89:91:50",
0
],
"shift": 5
},
"_meta": {
"title": "ModelSamplingSD3"
}
},
"89:91:52": {
"class_type": "ModelSamplingSD3",
"inputs": {
"model": [
"89:91:49",
0
],
"shift": 5
},
"_meta": {
"title": "ModelSamplingSD3"
}
},
"89:91:53": {
"class_type": "CLIPLoader",
"inputs": {
"clip_name": "umt5_xxl_fp8_e4m3fn_scaled.safetensors",
"device": "default",
"type": "wan"
},
"_meta": {
"title": "Load CLIP"
}
},
"89:91:54": {
"class_type": "VAELoader",
"inputs": {
"vae_name": "wan_2.1_vae.safetensors"
},
"_meta": {
"title": "Load VAE"
}
},
"89:91:56": {
"class_type": "CreateVideo",
"inputs": {
"bit_depth": "auto",
"codec": "none",
"color_space": "sRGB",
"fps": 24,
"images": [
"89:91:33",
0
]
},
"_meta": {
"title": "Create Video"
}
}
})
# run() also passes api_key to the partner nodes in this workflow.
job = client.run(workflow, api_key=api_key)よくある質問
High-resolution images with clear focal points tend to produce the best results, as they provide more detail for the AI to work with.
Yes, you can adjust the frame rate in the SaveVideo node to control the speed of the loop.
This workflow focuses on visual output. However, you can add audio using video editing software after exporting the video.
Use the PreviewImage node to check transitions between frames and adjust parameters in the 3313ce14-acfd-4a9c-9f0c-a1b01147e934 node for smoother results.