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.