LTX-2 Image to Video (Distilled)
Transform static images into dynamic videos with synchronized audio-video generation using LTX-2 distilled model. Optimized for faster generation while maintaining quality. Features expressive lip sync, natural motion, and improved speed.
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({
"75": {
"class_type": "SaveVideo",
"inputs": {
"filename_prefix": "video/LTX_2.0_i2v",
"format": "auto",
"format.codec": "auto",
"video": [
"92:97",
0
]
},
"_meta": {
"title": "Save Video"
}
},
"98": {
"class_type": "LoadImage",
"inputs": {
"image": "UPLOADED_image_FILENAME"
},
"_meta": {
"title": "Load Image"
}
},
"102": {
"class_type": "ResizeImageMaskNode",
"inputs": {
"input": [
"98",
0
],
"resize_type": "scale dimensions",
"resize_type.crop": "center",
"resize_type.height": 720,
"resize_type.width": 1280,
"scale_method": "lanczos"
},
"_meta": {
"title": "Resize Image/Mask"
}
},
"92:1": {
"class_type": "CheckpointLoaderSimple",
"inputs": {
"ckpt_name": "ltx-2-19b-distilled.safetensors"
},
"_meta": {
"title": "Load Checkpoint"
}
},
"92:105": {
"class_type": "GetImageSize",
"inputs": {
"image": [
"102",
0
]
},
"_meta": {
"title": "Get Image Size"
}
},
"92:106": {
"class_type": "ResizeImagesByLongerEdge",
"inputs": {
"images": [
"102",
0
],
"longer_edge": 1536
},
"_meta": {
"title": "Resize Images by Longer Edge (DEPRECATED)"
}
},
"92:107": {
"class_type": "LTXVImgToVideoInplace",
"inputs": {
"bypass": False,
"image": [
"92:99",
0
],
"latent": [
"92:43",
0
],
"strength": 1,
"vae": [
"92:1",
2
]
},
"_meta": {
"title": "LTXVImgToVideoInplace"
}
},
"92:108": {
"class_type": "LTXVImgToVideoInplace",
"inputs": {
"bypass": False,
"image": [
"92:99",
0
],
"latent": [
"92:84",
0
],
"strength": 1,
"vae": [
"92:1",
2
]
},
"_meta": {
"title": "LTXVImgToVideoInplace"
}
},
"92:11": {
"class_type": "RandomNoise",
"inputs": {
"noise_seed": 10
},
"_meta": {
"title": "RandomNoise"
}
},
"92:112": {
"class_type": "ConditioningZeroOut",
"inputs": {
"conditioning": [
"92:3",
0
]
},
"_meta": {
"title": "Conditioning Zero Out"
}
},
"92:113": {
"class_type": "ManualSigmas",
"inputs": {
"sigmas": "1., 0.99375, 0.9875, 0.98125, 0.975, 0.909375, 0.725, 0.421875, 0.0"
},
"_meta": {
"title": "ManualSigmas"
}
},
"92:22": {
"class_type": "LTXVConditioning",
"inputs": {
"frame_rate": 25,
"negative": [
"92:112",
0
],
"positive": [
"92:3",
0
]
},
"_meta": {
"title": "LTXVConditioning"
}
},
"92:3": {
"class_type": "CLIPTextEncode",
"inputs": {
"clip": [
"92:60",
0
],
"text": "One continuous shot. The enduro rider flies through the air over the black volcanic ridge, the front wheel dipping as the bike comes down, then lands hard on the wet track. The suspension compresses, mud and grit spray out from both tyres, and he leans forward, opens the throttle and rides away along the same ridge toward the low sun breaking through the storm clouds. The camera pans smoothly to follow the bike from the side as it falls and lands, then keeps tracking it in the same setting as it speeds off, with the rain mist and golden light staying constant. Sound of the engine revving hard, the heavy thud of the landing, gravel spraying, wind gusting past the camera and distant thunder over the empty plain."
},
"_meta": {
"title": "CLIP Text Encode (Prompt)"
}
},
"92:41": {
"class_type": "SamplerCustomAdvanced",
"inputs": {
"guider": [
"92:47",
0
],
"latent_image": [
"92:56",
0
],
"noise": [
"92:11",
0
],
"sampler": [
"92:8",
0
],
"sigmas": [
"92:113",
0
]
},
"_meta": {
"title": "SamplerCustomAdvanced"
}
},
"92:43": {
"class_type": "EmptyLTXVLatentVideo",
"inputs": {
"batch_size": 1,
"height": [
"92:91",
1
],
"length": [
"92:62",
0
],
"width": [
"92:91",
0
]
},
"_meta": {
"title": "Empty LTXV Latent Video"
}
},
"92:47": {
"class_type": "CFGGuider",
"inputs": {
"cfg": 1,
"model": [
"92:1",
0
],
"negative": [
"92:22",
1
],
"positive": [
"92:22",
0
]
},
"_meta": {
"title": "CFG Guider"
}
},
"92:48": {
"class_type": "LTXVAudioVAELoader",
"inputs": {
"ckpt_name": "ltx-2-19b-distilled.safetensors"
},
"_meta": {
"title": "Load LTXV Audio VAE"
}
},
"92:51": {
"class_type": "LTXVEmptyLatentAudio",
"inputs": {
"audio_vae": [
"92:48",
0
],
"batch_size": 1,
"frame_rate": 25,
"frames_number": [
"92:62",
0
]
},
"_meta": {
"title": "Empty LTXV Latent Audio"
}
},
"92:56": {
"class_type": "LTXVConcatAVLatent",
"inputs": {
"audio_latent": [
"92:51",
0
],
"video_latent": [
"92:107",
0
]
},
"_meta": {
"title": "Concat AV Latent"
}
},
"92:60": {
"class_type": "LTXAVTextEncoderLoader",
"inputs": {
"ckpt_name": "ltx-2-19b-distilled.safetensors",
"device": "default",
"text_encoder": "gemma_3_12B_it_fp4_mixed.safetensors"
},
"_meta": {
"title": "Load LTXV Audio Text Encoder"
}
},
"92:62": {
"class_type": "PrimitiveInt",
"inputs": {
"value": 121
},
"_meta": {
"title": "Length"
}
},
"92:66": {
"class_type": "KSamplerSelect",
"inputs": {
"sampler_name": "gradient_estimation"
},
"_meta": {
"title": "KSamplerSelect"
}
},
"92:67": {
"class_type": "RandomNoise",
"inputs": {
"noise_seed": 0
},
"_meta": {
"title": "RandomNoise"
}
},
"92:70": {
"class_type": "SamplerCustomAdvanced",
"inputs": {
"guider": [
"92:82",
0
],
"latent_image": [
"92:83",
0
],
"noise": [
"92:67",
0
],
"sampler": [
"92:66",
0
],
"sigmas": [
"92:73",
0
]
},
"_meta": {
"title": "SamplerCustomAdvanced"
}
},
"92:73": {
"class_type": "ManualSigmas",
"inputs": {
"sigmas": "0.909375, 0.725, 0.421875, 0.0"
},
"_meta": {
"title": "ManualSigmas"
}
},
"92:76": {
"class_type": "LatentUpscaleModelLoader",
"inputs": {
"model_name": "ltx-2-spatial-upscaler-x2-1.0.safetensors"
},
"_meta": {
"title": "Load Latent Upscale Model"
}
},
"92:8": {
"class_type": "KSamplerSelect",
"inputs": {
"sampler_name": "euler"
},
"_meta": {
"title": "KSamplerSelect"
}
},
"92:80": {
"class_type": "LTXVSeparateAVLatent",
"inputs": {
"av_latent": [
"92:41",
0
]
},
"_meta": {
"title": "Separate AV Latent"
}
},
"92:81": {
"class_type": "LTXVCropGuides",
"inputs": {
"latent": [
"92:80",
0
],
"negative": [
"92:22",
1
],
"positive": [
"92:22",
0
]
},
"_meta": {
"title": "LTXVCropGuides"
}
},
"92:82": {
"class_type": "CFGGuider",
"inputs": {
"cfg": 1,
"model": [
"92:1",
0
],
"negative": [
"92:81",
1
],
"positive": [
"92:81",
0
]
},
"_meta": {
"title": "CFG Guider"
}
},
"92:83": {
"class_type": "LTXVConcatAVLatent",
"inputs": {
"audio_latent": [
"92:80",
1
],
"video_latent": [
"92:108",
0
]
},
"_meta": {
"title": "Concat AV Latent"
}
},
"92:84": {
"class_type": "LTXVLatentUpsampler",
"inputs": {
"samples": [
"92:81",
2
],
"upscale_model": [
"92:76",
0
],
"vae": [
"92:1",
2
]
},
"_meta": {
"title": "spatial"
}
},
"92:89": {
"class_type": "EmptyImage",
"inputs": {
"batch_size": 1,
"color": 0,
"height": [
"92:105",
1
],
"width": [
"92:105",
0
]
},
"_meta": {
"title": "Empty Image"
}
},
"92:90": {
"class_type": "ImageScaleBy",
"inputs": {
"image": [
"92:89",
0
],
"scale_by": 0.5,
"upscale_method": "lanczos"
},
"_meta": {
"title": "Upscale Image By"
}
},
"92:91": {
"class_type": "GetImageSize",
"inputs": {
"image": [
"92:90",
0
]
},
"_meta": {
"title": "Get Image Size"
}
},
"92:94": {
"class_type": "LTXVSeparateAVLatent",
"inputs": {
"av_latent": [
"92:70",
1
]
},
"_meta": {
"title": "Separate AV Latent"
}
},
"92:95": {
"class_type": "VAEDecode",
"inputs": {
"samples": [
"92:94",
0
],
"vae": [
"92:1",
2
]
},
"_meta": {
"title": "VAE Decode"
}
},
"92:96": {
"class_type": "LTXVAudioVAEDecode",
"inputs": {
"audio_vae": [
"92:48",
0
],
"samples": [
"92:94",
1
]
},
"_meta": {
"title": "LTXV Audio VAE Decode"
}
},
"92:97": {
"class_type": "CreateVideo",
"inputs": {
"audio": [
"92:96",
0
],
"bit_depth": "auto",
"codec": "none",
"color_space": "sRGB",
"fps": 25,
"images": [
"92:95",
0
]
},
"_meta": {
"title": "Create Video"
}
},
"92:99": {
"class_type": "LTXVPreprocess",
"inputs": {
"image": [
"92:106",
0
],
"img_compression": 33
},
"_meta": {
"title": "LTXV Preprocess"
}
}
})
workflow.set_input("98", "image", client.assets.from_url("https://comfy.org/catalog-media/9a3b7fb5-dad7-490b-8cd9-50f0b6cbeaed/71bdcc61-f097-4ecf-a8c8-e14fd79c59cc"))
# 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("75"):
output.to_file(output.name)Try an example
About this workflow
This ComfyUI workflow turns a single still image into a dynamic video using the distilled LTX-2 model from Lightricks. It wraps the animation pipeline inside a Subgraph node (b7c2d337-c38d-4c04-922b-2d638449d13e) so you can focus on prompting and parameters instead of wiring low-level components. Your source is loaded with LoadImage, conformed to a video-safe size using ResizeImageMaskNode, then animated by the LTX-2 Distilled Subgraph, which generates temporally coherent frames with expressive motion and lip sync.
The resulting video (and audio, when provided by the model) is written to disk with SaveVideo. A MarkdownNote in the canvas provides inline guidance and links to Subgraph docs to help you understand and customize the encapsulated graph.
Questions & answers
Set width and height to values divisible by 64 (e.g., 1280x768, 1920x1088). Use a frame count of the form 8n+1 (e.g., 9, 17, 25, 33). If you enter invalid values, the Subgraph will automatically choose the closest valid parameters, so you won’t see an error but your result may not match the exact numbers you typed.
The LTX-2 distilled Subgraph can produce expressive lip sync when your prompt includes dialogue in the audio section. When the model returns an audio track, SaveVideo will mux it into the output. If your model build/weights do not output audio, you will get a silent video (you can add audio later in an editor).
Use the distilled model’s default 720p for faster runs, keep prompts precise about actions over time, and choose a moderate frame count like 17 or 25. If you upscale to 1920x1088, expect longer times. For stability, keep the seed fixed while iterating and avoid overly aggressive motion descriptions in a single short clip.
This workflow is designed to be forgiving: it silently snaps to the nearest valid width/height (multiples of 64) and to frame counts of 8n+1. Double-check the Subgraph’s effective settings after running, and adjust your inputs to the nearest valid target if you need a specific result.