Seamless Video Transitions - LTX 2.3 FLW LoRA
Upload two input videos and generate a seamless transition between the two shots.
简介
This ComfyUI workflow creates a seamless, learned transition between two input clips using an LTX 2.3 FLW LoRA. You feed one video into each LoadVideo node, and a dedicated transition node (UUID: 63482e7d-23c9-4396-a7a2-cd4ca2316eac) synthesizes a smooth handoff from the end of the first shot to the start of the second. The SaveVideo node then assembles the processed frames into a single output clip. A built-in MarkdownNote provides an at-a-glance guide to recommended settings and practical tips.
Technically, the workflow performs diffusion-based video-to-video generation guided by the LTX 2.3 FLW LoRA to harmonize color, texture, and motion across the cut. For temporal consistency, inference runs over the full frame range (not just the transition window). That design keeps the look stable across the entire sequence but also means longer clips and higher resolutions will increase runtime and VRAM use. You’ll typically set the in/out ranges for each source clip, define a transition length, and adjust the LoRA influence/strength and seed in the transition node.
SaveVideo controls the output frame rate and file name for export.
你需要提供
- 正在读取工作流
在 ComfyUI 中运行。 此工作流无法在本页面运行。你可以在 Comfy Cloud 中打开(已预先加载所需内容),或下载后在自己的 ComfyUI 中使用。
在 Cloud 中试用查看工作流内部
节点图的只读预览。在 Comfy Cloud 中打开或下载后即可编辑节点。
Loading the graph
Open full-size workflow preview- 作者
- Rob
需要自定义节点
- 正在读取工作流
通过代码使用此工作流
尽可能根据工作流图和输入自动生成代码,否则显示已保存的示例。运行代码可能产生费用,请先核对凭据和端点。
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({
"116": {
"class_type": "SaveVideo",
"inputs": {
"codec": "auto",
"filename_prefix": "video/ComfyUI",
"format": "auto",
"format.codec": "auto",
"video": [
"108:122",
0
]
},
"_meta": {
"title": "Save Video"
}
},
"117": {
"class_type": "LoadVideo",
"inputs": {
"file": "7c1b43d5af480f29a8b75424ae10700e7216a1649759ed03aab61be8ee1a845e.mp4"
},
"_meta": {
"title": "Load Shot 1"
}
},
"118": {
"class_type": "LoadVideo",
"inputs": {
"file": "ab49a07d4e1faaa0f1f1cc60e3ad1edef70bdd41da40d1c0a27b0620a2e52493.mp4"
},
"_meta": {
"title": "Load Shot 2"
}
},
"108:1": {
"class_type": "DiffusionModelLoaderKJ",
"inputs": {
"compute_dtype": "bf16",
"enable_fp16_accumulation": False,
"model_name": "ltx-2.3-22b-dev_transformer_only_bf16.safetensors",
"patch_cublaslinear": False,
"sage_attention": "disabled",
"weight_dtype": "default"
},
"_meta": {
"title": "Diffusion Model Loader KJ"
}
},
"108:10": {
"class_type": "LTXVConditioning",
"inputs": {
"frame_rate": 24,
"negative": [
"108:9",
0
],
"positive": [
"108:8",
0
]
},
"_meta": {
"title": "LTXVConditioning"
}
},
"108:107": {
"class_type": "SimpleCalculatorKJ",
"inputs": {
"expression": "round((a + b + c - 1) / 8) * 8 + 1",
"variables.a": [
"108:109",
2
],
"variables.b": [
"108:111",
2
],
"variables.c": [
"108:125",
0
]
},
"_meta": {
"title": "TOTAL FRAMES"
}
},
"108:109": {
"class_type": "GetImageSize",
"inputs": {
"image": [
"108:120",
0
]
},
"_meta": {
"title": "Get Image Size"
}
},
"108:111": {
"class_type": "GetImageSize",
"inputs": {
"image": [
"108:121",
0
]
},
"_meta": {
"title": "Get Image Size"
}
},
"108:120": {
"class_type": "GetVideoComponents",
"inputs": {
"video": [
"117",
0
]
},
"_meta": {
"title": "Get Video Components"
}
},
"108:121": {
"class_type": "GetVideoComponents",
"inputs": {
"video": [
"118",
0
]
},
"_meta": {
"title": "Get Video Components"
}
},
"108:122": {
"class_type": "CreateVideo",
"inputs": {
"audio": [
"108:25",
0
],
"bit_depth": "auto",
"codec": "none",
"color_space": "sRGB",
"fps": 24,
"images": [
"108:24",
0
]
},
"_meta": {
"title": "Create Video"
}
},
"108:125": {
"class_type": "PrimitiveInt",
"inputs": {
"value": 48
},
"_meta": {
"title": "transition frames"
}
},
"108:13": {
"class_type": "LTXVAddGuide",
"inputs": {
"frame_idx": 0,
"image": [
"108:74",
0
],
"latent": [
"108:14",
0
],
"negative": [
"108:10",
1
],
"positive": [
"108:10",
0
],
"strength": 1,
"vae": [
"108:5",
0
]
},
"_meta": {
"title": "LTXVAddGuide"
}
},
"108:14": {
"class_type": "EmptyLTXVLatentVideo",
"inputs": {
"batch_size": 1,
"height": [
"108:74",
2
],
"length": [
"108:74",
3
],
"width": [
"108:74",
1
]
},
"_meta": {
"title": "Empty LTXV Latent Video"
}
},
"108:15": {
"class_type": "LTXVConcatAVLatent",
"inputs": {
"audio_latent": [
"108:29",
0
],
"video_latent": [
"108:13",
2
]
},
"_meta": {
"title": "Concat AV Latent"
}
},
"108:17": {
"class_type": "SamplerCustomAdvanced",
"inputs": {
"guider": [
"108:19",
0
],
"latent_image": [
"108:15",
0
],
"noise": [
"108:18",
0
],
"sampler": [
"108:20",
0
],
"sigmas": [
"108:21",
0
]
},
"_meta": {
"title": "SamplerCustomAdvanced"
}
},
"108:18": {
"class_type": "RandomNoise",
"inputs": {
"noise_seed": 240378926876440
},
"_meta": {
"title": "RandomNoise"
}
},
"108:19": {
"class_type": "CFGGuider",
"inputs": {
"cfg": 1,
"model": [
"108:4",
0
],
"negative": [
"108:13",
1
],
"positive": [
"108:13",
0
]
},
"_meta": {
"title": "CFG Guider"
}
},
"108:20": {
"class_type": "KSamplerSelect",
"inputs": {
"sampler_name": "euler_ancestral_cfg_pp"
},
"_meta": {
"title": "KSamplerSelect"
}
},
"108:21": {
"class_type": "BasicScheduler",
"inputs": {
"denoise": 1,
"model": [
"108:4",
0
],
"scheduler": "linear_quadratic",
"steps": 8
},
"_meta": {
"title": "BasicScheduler"
}
},
"108:23": {
"class_type": "LTXVSeparateAVLatent",
"inputs": {
"av_latent": [
"108:17",
0
]
},
"_meta": {
"title": "Separate AV Latent"
}
},
"108:24": {
"class_type": "VAEDecode",
"inputs": {
"samples": [
"108:31",
2
],
"vae": [
"108:5",
0
]
},
"_meta": {
"title": "VAE Decode"
}
},
"108:25": {
"class_type": "LTXVAudioVAEDecode",
"inputs": {
"audio_vae": [
"108:6",
0
],
"samples": [
"108:23",
1
]
},
"_meta": {
"title": "LTXV Audio VAE Decode"
}
},
"108:29": {
"class_type": "SetLatentNoiseMask",
"inputs": {
"mask": [
"108:30",
0
],
"samples": [
"108:98",
0
]
},
"_meta": {
"title": "Set Latent Noise Mask"
}
},
"108:30": {
"class_type": "SolidMask",
"inputs": {
"height": [
"108:74",
2
],
"value": 0,
"width": [
"108:74",
1
]
},
"_meta": {
"title": "Create Solid Mask"
}
},
"108:31": {
"class_type": "LTXVCropGuides",
"inputs": {
"latent": [
"108:23",
0
],
"negative": [
"108:13",
0
],
"positive": [
"108:13",
0
]
},
"_meta": {
"title": "LTXVCropGuides"
}
},
"108:33": {
"class_type": "DualCLIPLoader",
"inputs": {
"clip_name1": "gemma_3_12B_it.safetensors",
"clip_name2": "ltx-2.3_text_projection_bf16.safetensors",
"device": "default",
"type": "ltxv"
},
"_meta": {
"title": "Load CLIP (Dual)"
}
},
"108:4": {
"class_type": "LoraLoaderModelOnly",
"inputs": {
"lora_name": "ltx-2.3-22b-distilled-1.1_lora-dynamic_fro09_avg_rank_111_bf16.safetensors",
"model": [
"108:49",
0
],
"strength_model": 0.5
},
"_meta": {
"title": "Load LoRA"
}
},
"108:49": {
"class_type": "LoraLoaderModelOnly",
"inputs": {
"lora_name": "systms__SYSTMS-FLW-IC-LORA-LTX-23__SYSTMS_FLW_V1_LTX23.safetensors",
"model": [
"108:1",
0
],
"strength_model": 1
},
"_meta": {
"title": "Load LoRA"
}
},
"108:5": {
"class_type": "VAELoaderKJ",
"inputs": {
"device": "main_device",
"vae_name": "LTX23_video_vae_bf16.safetensors",
"weight_dtype": "bf16"
},
"_meta": {
"title": "VAELoader KJ"
}
},
"108:6": {
"class_type": "VAELoaderKJ",
"inputs": {
"device": "main_device",
"vae_name": "LTX23_audio_vae_bf16.safetensors",
"weight_dtype": "bf16"
},
"_meta": {
"title": "VAELoader KJ"
}
},
"108:61": {
"class_type": "GeminiNode",
"inputs": {
"model": "gemini-3-1-flash-lite",
"prompt": "Your task is to write an LTX 2.3 LoRA video prompt that describes the attached video, inferring a seamless morphing transition where the gray area is, using the following formula:\n\n[Action A] seamlessly transforms into [Action B]. \n[Subject A description] becomes [Subject B description]. \n[Setting A] shifts to [Setting B]. \n[Camera behavior across the transition]. \n[Lighting/mood continuity].\n\nHere is an example of what that might look like:\n\nA motocross rider performing a mid-air backflip seamlessly transforms into a man standing and holding a basketball. The upside-down rider on a dirt bike becomes a young man in a black t-shirt and white shorts. A mountainous landscape over a lake shifts to an outdoor urban basketball court with a city skyline. The camera holds steady through the morph, keeping the central figure framed. Soft evening daylight throughout, continuous and even.\n\nRETURN THE PROMPT ONLY",
"seed": 42,
"system_prompt": "",
"video": [
"108:63",
0
]
},
"_meta": {
"title": "Google Gemini"
}
},
"108:63": {
"class_type": "CreateVideo",
"inputs": {
"bit_depth": "auto",
"codec": "none",
"color_space": "sRGB",
"fps": 24,
"images": [
"108:74",
0
]
},
"_meta": {
"title": "Create Video"
}
},
"108:71": {
"class_type": "WanVideoVACEStartToEndFrame",
"inputs": {
"empty_frame_level": 0.5,
"end_image": [
"108:82",
0
],
"end_index": -1,
"num_frames": [
"108:107",
1
],
"start_image": [
"108:81",
0
],
"start_index": 0
},
"_meta": {
"title": "WanVideo VACE Start To End Frame"
}
},
"108:74": {
"class_type": "GetImageSizeAndCount",
"inputs": {
"image": [
"108:71",
0
]
},
"_meta": {
"title": "Get Image Size & Count"
}
},
"108:8": {
"class_type": "CLIPTextEncode",
"inputs": {
"clip": [
"108:33",
0
],
"text": [
"108:80",
0
]
},
"_meta": {
"title": "CLIP Text Encode (Prompt)"
}
},
"108:80": {
"class_type": "JoinStrings",
"inputs": {
"delimiter": " ",
"string1": [
"108:86",
0
],
"string2": [
"108:61",
0
]
},
"_meta": {
"title": "Join Strings"
}
},
"108:81": {
"class_type": "ImageResizeKJv2",
"inputs": {
"crop_position": "center",
"device": "cpu",
"divisible_by": 32,
"height": 0,
"image": [
"108:120",
0
],
"keep_proportion": "resize",
"pad_color": "0, 0, 0",
"upscale_method": "lanczos",
"width": 1280
},
"_meta": {
"title": "Resize Image v2"
}
},
"108:82": {
"class_type": "ImageResizeKJv2",
"inputs": {
"crop_position": "center",
"device": "cpu",
"divisible_by": 32,
"height": [
"108:81",
2
],
"image": [
"108:121",
0
],
"keep_proportion": "crop",
"pad_color": "0, 0, 0",
"upscale_method": "lanczos",
"width": [
"108:81",
1
]
},
"_meta": {
"title": "Resize Image v2"
}
},
"108:86": {
"class_type": "PrimitiveString",
"inputs": {
"value": "FLW,"
},
"_meta": {
"title": "Text"
}
},
"108:9": {
"class_type": "CLIPTextEncode",
"inputs": {
"clip": [
"108:33",
0
],
"text": ""
},
"_meta": {
"title": "CLIP Text Encode (Prompt)"
}
},
"108:96": {
"class_type": "LoadAudio",
"inputs": {
"audio": "ddc03eca36d627c209d416a515328420b2b52411ac4108498843d0316a22bb8c.mp3"
},
"_meta": {
"title": "Load Audio"
}
},
"108:98": {
"class_type": "LTXVAudioVAEEncode",
"inputs": {
"audio": [
"108:96",
0
],
"audio_vae": [
"108:6",
0
]
},
"_meta": {
"title": "LTXV Audio VAE Encode"
}
}
})
# run() also passes api_key to the partner nodes in this workflow.
job = client.run(workflow, api_key=api_key)常见问答
To maintain temporal consistency. Running inference across the full frame range helps keep the look stable and reduces popping before and after the handoff. If you need faster runs, trim your inputs to just the necessary ranges, reduce resolution/FPS, or shorten the clips.
Try increasing the transition duration, lowering the LoRA influence/strength, and locking the seed for repeatable results. Place the cut around frames with similar composition (camera angle, subject size), and ensure both inputs share the same resolution and FPS.
Requirements scale with resolution, clip length, and transition duration. Start with short clips at a modest resolution to gauge performance. Longer sequences or HD+ resolutions benefit from GPUs with more VRAM; if you hit limits, downscale or shorten the frame ranges.
The SaveVideo node outputs a silent video. If you need audio, export the video and reattach audio from one of the source clips (or a music track) in your video editor.