Generate UGC Video With Voice Clone
Upload an image to generate AI prompts and synthesize speech. Create a final video with accurate lip sync using Eleven Labs and LTX Video.
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
- Mixed outputs
- 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({
"3": {
"class_type": "ElevenLabsTextToSpeech",
"inputs": {
"apply_text_normalization": "auto",
"language_code": "",
"model": "eleven_v3",
"model.similarity_boost": 0.75,
"model.speed": 1,
"output_format": "mp3_44100_192",
"seed": 3333,
"stability": 0.5,
"text": [
"475",
0
],
"voice": [
"442",
0
]
},
"_meta": {
"title": "ElevenLabs Text to Speech"
}
},
"440": {
"class_type": "LoadImage",
"inputs": {
"image": "UPLOADED_image_FILENAME"
},
"_meta": {
"title": "Load Image"
}
},
"441": {
"class_type": "GeminiNode",
"inputs": {
"images": [
"440",
0
],
"model": "gemini-3-1-pro",
"prompt": "A friendly, honest product review for a small amber jar of overnight repair cream, filmed like a casual morning video at home. She has just woken up on a rainy day, holds the jar up next to her cheek and talks straight to the camera like she is telling a friend about something she genuinely loves. Open with a quick, surprising hook about waking up with skin that looks rested even after a short night. Mention one clear benefit: it soaks in fast and is not greasy, so she can put it on and go straight to bed. Keep the tone warm, a little playful and relaxed, with a small laugh. End with a simple, light recommendation to try it, no hard sell and no prices.",
"seed": 967822922709019,
"system_prompt": "Role: You are a professional prompt writer for Eleven Labs v3 two-part generation.\n\nTask: Generate a two-part prompt from the user’s input.\n\nStrict Output Constraint (IMPORTANT):\n\nReturn the output as RAW PLAIN TEXT only.\nDO NOT use Markdown code blocks.\nDO NOT use any headers, labels, or bold text outside of the generated two-part output.\nUse ONLY the separator --- to divide the two parts.\nPart 1: spoken dialogue/performance text only, optimized for Eleven v3.\nPart 2: a structured scene breakdown in short field lines.\nDo NOT output anything before Part 1 or after Part 2.\n\nGlobal Length Rule:\n\nKeep the final generated prompt short enough to produce about 10 seconds of audio.\nAim for roughly 20 to 35 spoken words in Part 1 unless the user explicitly asks otherwise.\nCondense long inputs aggressively while preserving the core meaning, tone, and key message.\nPrioritize brevity, clarity, speakability, and a strong opening hook over completeness.\nRemove repetition, side details, filler, and nonessential context.\nIf the input is too long, compress it into a concise spoken version rather than preserving everything.\nDo not create Part 1 output that would likely exceed about 10 seconds of speech.\n\nPart 1 Rules:\n\nPart 1 must contain ONLY the words meant to be spoken aloud.\nDo NOT begin Part 1 with visual description, scene-setting narration, character description, or action description.\nStart immediately with the most attention-grabbing, high-impact spoken hook.\nThe hook should appear as early as possible, ideally in the first sentence or phrase.\nDo NOT describe what the character looks like, where they are, what they are wearing, what they are holding, or what is happening visually unless that information is spoken by the character as part of the dialogue itself.\nPart 1 should read like a real spoken performance, monologue, ad read, or direct address.\nPart 1 may use Eleven v3-compatible vocal tags in square brackets, such as [laughs], [whispers], [sighs], [excited], [curious], [sarcastic], [exhales], [crying], [snorts], [mischievously], [sings], when helpful.\nUse punctuation, ellipses, pauses, and selective capitalization to improve spoken delivery when useful.\nDo NOT use SSML.\nDo NOT use non-voice tags such as music, ambience, sound effects, camera notes, or technical markup.\nCompress long user text into the shortest strong spoken version that preserves the selling point or core message.\nPrefer one clear hook, one core benefit, and one short closing beat.\n\nPart 2 Rules:\n\nPart 2 must transform the same idea into a compact structured format.\nPart 2 must strongly bias toward UGC-style action and framing and prnounce the character is talking into the camera.\nPart 2 must describe the subject as if they are casually filming a real user-generated testimonial, ad, or selfie-style clip.\nPrioritize natural human behavior such as:\ntalking directly to camera\nhandheld or phone-like framing\ncasual presenting of product\nnatural gestures\nauthentic facial expression\nslight body movement\nconversational delivery\nAvoid cinematic, polished commercial, theatrical, or overly staged action unless the user explicitly asks for that.\nKeep the action grounded, believable, and creator-like rather than scripted or broadcast-like.\nEach line should follow this pattern: field name, colon, content.\nUse only concise production-style descriptors.\nKeep Part 2 aligned with Part 1 and do not introduce unrelated ideas.\nPrefer fields such as:\nscene:\ncharacter:\naction:\ncamera:\ntone:\nUse action: to emphasize the UGC performance behavior most strongly.\nUse camera: to emphasize casual capture style, such as handheld, selfie framing, direct-to-camera, slight natural movement, or eye-level creator framing.\nKeep wardrobe and appearance brief unless they are essential to the concept.\nKeep Part 2 compact and production-friendly.\n\nUGC Bias Rules for Part 2:\n\nDefault to direct-to-camera testimonial energy unless the user asks otherwise.\nDefault to natural handheld realism unless the user asks otherwise.\nDefault to real-person creator behavior instead of actor blocking unless the user asks otherwise.\nPrefer verbs like:\ntalking, holding, showing, gesturing, reacting, smiling, leaning in, presenting, pointing\nAvoid verbs like:\nposing, performing choreography, dramatically turning, stylized modeling\nMake the structured breakdown help the model generate believable UGC action, not just static appearance.\n\nConsistency Rules:\n\nPart 1 and Part 2 must describe the same scene and intent.\nPart 2 is a clean structural breakdown of Part 1.\nDo not make Part 1 and Part 2 feel like separate concepts.\nPreserve the user’s intent, subject, tone, setting, and spoken energy across both parts.\n\nSafety and Quality Rules:\n\nDo not introduce sensitive, explicit, hateful, political, or unsafe content that was not already in the user input.\nKeep vocal tags contextually appropriate and naturally speakable.\nUse expressive enhancements sparingly and intentionally.\nEnsure the final output always contains exactly two parts separated by ---."
},
"_meta": {
"title": "Google Gemini"
}
},
"442": {
"class_type": "ElevenLabsVoiceSelector",
"inputs": {
"voice": "Bella (female, american)"
},
"_meta": {
"title": "ElevenLabs Voice Selector"
}
},
"443": {
"class_type": "PreviewAny",
"inputs": {
"source": [
"441",
0
]
},
"_meta": {
"title": "Preview as Text"
}
},
"445": {
"class_type": "SaveAudioMP3",
"inputs": {
"audio": [
"3",
0
],
"filename_prefix": "audio/text2speech2video",
"quality": "V0"
},
"_meta": {
"title": "Save Audio (MP3) (DEPRECATED)"
}
},
"473": {
"class_type": "PreviewAny",
"inputs": {
"source": [
"476",
0
]
},
"_meta": {
"title": "Preview as Text"
}
},
"474": {
"class_type": "PreviewAny",
"inputs": {
"source": [
"475",
0
]
},
"_meta": {
"title": "Preview as Text"
}
},
"475": {
"class_type": "RegexExtract",
"inputs": {
"case_insensitive": True,
"dotall": False,
"group_index": 1,
"mode": "First Group",
"multiline": False,
"regex_pattern": "(?sm)^(.*?)^---$(.*)",
"string": [
"441",
0
]
},
"_meta": {
"title": "Extract Text"
}
},
"476": {
"class_type": "RegexExtract",
"inputs": {
"case_insensitive": True,
"dotall": False,
"group_index": 1,
"mode": "First Group",
"multiline": False,
"regex_pattern": "(?s)(?<=\\n---\\n)([\\s\\S]*)$",
"string": [
"441",
0
]
},
"_meta": {
"title": "Extract Text"
}
},
"479": {
"class_type": "SaveVideo",
"inputs": {
"filename_prefix": "video/ComfyUI",
"format": "auto",
"format.codec": "auto",
"video": [
"478:312",
0
]
},
"_meta": {
"title": "Save Video"
}
},
"478:285": {
"class_type": "RandomNoise",
"inputs": {
"noise_seed": 42
},
"_meta": {
"title": "RandomNoise"
}
},
"478:286": {
"class_type": "RandomNoise",
"inputs": {
"noise_seed": 3333
},
"_meta": {
"title": "RandomNoise"
}
},
"478:287": {
"class_type": "LTXVConcatAVLatent",
"inputs": {
"audio_latent": [
"478:309",
1
],
"video_latent": [
"478:296",
0
]
},
"_meta": {
"title": "Concat AV Latent"
}
},
"478:288": {
"class_type": "KSamplerSelect",
"inputs": {
"sampler_name": "euler_cfg_pp"
},
"_meta": {
"title": "KSamplerSelect"
}
},
"478:289": {
"class_type": "ManualSigmas",
"inputs": {
"sigmas": "0.85, 0.7250, 0.4219, 0.0"
},
"_meta": {
"title": "ManualSigmas"
}
},
"478:290": {
"class_type": "CFGGuider",
"inputs": {
"cfg": 1,
"model": [
"478:601",
0
],
"negative": [
"478:292",
1
],
"positive": [
"478:292",
0
]
},
"_meta": {
"title": "CFG Guider"
}
},
"478:291": {
"class_type": "SamplerCustomAdvanced",
"inputs": {
"guider": [
"478:315",
0
],
"latent_image": [
"478:326",
0
],
"noise": [
"478:286",
0
],
"sampler": [
"478:298",
0
],
"sigmas": [
"478:308",
0
]
},
"_meta": {
"title": "SamplerCustomAdvanced"
}
},
"478:292": {
"class_type": "LTXVCropGuides",
"inputs": {
"latent": [
"478:309",
0
],
"negative": [
"478:307",
1
],
"positive": [
"478:307",
0
]
},
"_meta": {
"title": "LTXVCropGuides"
}
},
"478:293": {
"class_type": "LoraLoaderModelOnly",
"inputs": {
"lora_name": "ltx-2.3-22b-distilled-lora-384.safetensors",
"model": [
"478:317",
0
],
"strength_model": 0.5
},
"_meta": {
"title": "Load LoRA"
}
},
"478:294": {
"class_type": "ResizeImagesByLongerEdge",
"inputs": {
"images": [
"478:297",
0
],
"longer_edge": 1536
},
"_meta": {
"title": "Resize Images by Longer Edge (DEPRECATED)"
}
},
"478:295": {
"class_type": "LTXVLatentUpsampler",
"inputs": {
"samples": [
"478:309",
0
],
"upscale_model": [
"478:313",
0
],
"vae": [
"478:317",
2
]
},
"_meta": {
"title": "LTXVLatentUpsampler"
}
},
"478:296": {
"class_type": "LTXVImgToVideoInplace",
"inputs": {
"bypass": [
"478:305",
0
],
"image": [
"478:334",
0
],
"latent": [
"478:295",
0
],
"strength": 1,
"vae": [
"478:317",
2
]
},
"_meta": {
"title": "LTXVImgToVideoInplace"
}
},
"478:297": {
"class_type": "ResizeImageMaskNode",
"inputs": {
"input": [
"440",
0
],
"resize_type": "scale dimensions",
"resize_type.crop": "center",
"resize_type.height": [
"478:324",
0
],
"resize_type.width": [
"478:330",
0
],
"scale_method": "lanczos"
},
"_meta": {
"title": "Resize Image/Mask"
}
},
"478:298": {
"class_type": "KSamplerSelect",
"inputs": {
"sampler_name": "euler_ancestral_cfg_pp"
},
"_meta": {
"title": "KSamplerSelect"
}
},
"478:299": {
"class_type": "ComfyMathExpression",
"inputs": {
"expression": "a/2",
"values.a": [
"478:330",
0
]
},
"_meta": {
"title": "Math Expression"
}
},
"478:301": {
"class_type": "ComfyMathExpression",
"inputs": {
"expression": "a/2",
"values.a": [
"478:324",
0
]
},
"_meta": {
"title": "Math Expression"
}
},
"478:302": {
"class_type": "EmptyLTXVLatentVideo",
"inputs": {
"batch_size": 1,
"height": [
"478:301",
1
],
"length": [
"478:329",
1
],
"width": [
"478:299",
1
]
},
"_meta": {
"title": "Empty LTXV Latent Video"
}
},
"478:303": {
"class_type": "LTXVAudioVAEDecode",
"inputs": {
"audio_vae": [
"478:335",
0
],
"samples": [
"478:311",
1
]
},
"_meta": {
"title": "LTXV Audio VAE Decode"
}
},
"478:304": {
"class_type": "ComfyMathExpression",
"inputs": {
"expression": "a",
"values.a": [
"478:323",
0
]
},
"_meta": {
"title": "Math Expression"
}
},
"478:305": {
"class_type": "PrimitiveBoolean",
"inputs": {
"value": False
},
"_meta": {
"title": "Switch to Text to Video?"
}
},
"478:306": {
"class_type": "CLIPTextEncode",
"inputs": {
"clip": [
"478:318",
0
],
"text": [
"478:319",
0
]
},
"_meta": {
"title": "CLIP Text Encode (Prompt)"
}
},
"478:307": {
"class_type": "LTXVConditioning",
"inputs": {
"frame_rate": [
"478:304",
0
],
"negative": [
"478:314",
0
],
"positive": [
"478:306",
0
]
},
"_meta": {
"title": "LTXVConditioning"
}
},
"478:308": {
"class_type": "ManualSigmas",
"inputs": {
"sigmas": "1.0, 0.99375, 0.9875, 0.98125, 0.975, 0.909375, 0.725, 0.421875, 0.0"
},
"_meta": {
"title": "ManualSigmas"
}
},
"478:309": {
"class_type": "LTXVSeparateAVLatent",
"inputs": {
"av_latent": [
"478:291",
0
]
},
"_meta": {
"title": "Separate AV Latent"
}
},
"478:310": {
"class_type": "SamplerCustomAdvanced",
"inputs": {
"guider": [
"478:290",
0
],
"latent_image": [
"478:287",
0
],
"noise": [
"478:285",
0
],
"sampler": [
"478:288",
0
],
"sigmas": [
"478:289",
0
]
},
"_meta": {
"title": "SamplerCustomAdvanced"
}
},
"478:311": {
"class_type": "LTXVSeparateAVLatent",
"inputs": {
"av_latent": [
"478:310",
0
]
},
"_meta": {
"title": "Separate AV Latent"
}
},
"478:312": {
"class_type": "CreateVideo",
"inputs": {
"audio": [
"478:303",
0
],
"bit_depth": "auto",
"codec": "none",
"color_space": "sRGB",
"fps": [
"478:304",
0
],
"images": [
"478:316",
0
]
},
"_meta": {
"title": "Create Video"
}
},
"478:313": {
"class_type": "LatentUpscaleModelLoader",
"inputs": {
"model_name": "ltx-2.3-spatial-upscaler-x2-1.1.safetensors"
},
"_meta": {
"title": "Load Latent Upscale Model"
}
},
"478:314": {
"class_type": "CLIPTextEncode",
"inputs": {
"clip": [
"478:318",
0
],
"text": "pc game, console game, video game, cartoon, childish, ugly"
},
"_meta": {
"title": "CLIP Text Encode (Prompt)"
}
},
"478:315": {
"class_type": "CFGGuider",
"inputs": {
"cfg": 1,
"model": [
"478:601",
0
],
"negative": [
"478:307",
1
],
"positive": [
"478:307",
0
]
},
"_meta": {
"title": "CFG Guider"
}
},
"478:316": {
"class_type": "VAEDecodeTiled",
"inputs": {
"overlap": 64,
"samples": [
"478:311",
0
],
"temporal_overlap": 4,
"temporal_size": 4096,
"tile_size": 768,
"vae": [
"478:317",
2
]
},
"_meta": {
"title": "VAE Decode (Tiled)"
}
},
"478:317": {
"class_type": "CheckpointLoaderSimple",
"inputs": {
"ckpt_name": "ltx-2.3-22b-dev-fp8.safetensors"
},
"_meta": {
"title": "Load Checkpoint"
}
},
"478:318": {
"class_type": "LTXAVTextEncoderLoader",
"inputs": {
"ckpt_name": "ltx-2.3-22b-dev-fp8.safetensors",
"device": "default",
"text_encoder": "gemma_3_12B_it_fp4_mixed.safetensors"
},
"_meta": {
"title": "Load LTXV Audio Text Encoder"
}
},
"478:319": {
"class_type": "PrimitiveStringMultiline",
"inputs": {
"value": [
"476",
0
]
},
"_meta": {
"title": "Prompt"
}
},
"478:323": {
"class_type": "PrimitiveInt",
"inputs": {
"value": 24
},
"_meta": {
"title": "Frame Rate"
}
},
"478:324": {
"class_type": "PrimitiveInt",
"inputs": {
"value": 720
},
"_meta": {
"title": "Height"
}
},
"478:325": {
"class_type": "LTXVImgToVideoInplace",
"inputs": {
"bypass": [
"478:305",
0
],
"image": [
"478:334",
0
],
"latent": [
"478:302",
0
],
"strength": 0.7,
"vae": [
"478:317",
2
]
},
"_meta": {
"title": "LTXVImgToVideoInplace"
}
},
"478:326": {
"class_type": "LTXVConcatAVLatent",
"inputs": {
"audio_latent": [
"478:327",
0
],
"video_latent": [
"478:325",
0
]
},
"_meta": {
"title": "Concat AV Latent"
}
},
"478:327": {
"class_type": "SetLatentNoiseMask",
"inputs": {
"mask": [
"478:333",
0
],
"samples": [
"478:328",
0
]
},
"_meta": {
"title": "Set Latent Noise Mask"
}
},
"478:328": {
"class_type": "LTXVAudioVAEEncode",
"inputs": {
"audio": [
"478:332",
0
],
"audio_vae": [
"478:335",
0
]
},
"_meta": {
"title": "LTXV Audio VAE Encode"
}
},
"478:329": {
"class_type": "ComfyMathExpression",
"inputs": {
"expression": "a * b + 1",
"values.a": [
"478:331",
0
],
"values.b": [
"478:323",
0
]
},
"_meta": {
"title": "Math Expression"
}
},
"478:330": {
"class_type": "PrimitiveInt",
"inputs": {
"value": 1280
},
"_meta": {
"title": "Width"
}
},
"478:331": {
"class_type": "PrimitiveFloat",
"inputs": {
"value": 12
},
"_meta": {
"title": "Duration"
}
},
"478:332": {
"class_type": "TrimAudioDuration",
"inputs": {
"audio": [
"3",
0
],
"duration": [
"478:331",
0
],
"start_index": 0
},
"_meta": {
"title": "Trim Audio Duration"
}
},
"478:333": {
"class_type": "SolidMask",
"inputs": {
"height": [
"478:324",
0
],
"value": 0,
"width": [
"478:330",
0
]
},
"_meta": {
"title": "Create Solid Mask"
}
},
"478:334": {
"class_type": "LTXVPreprocess",
"inputs": {
"image": [
"478:294",
0
],
"img_compression": 18
},
"_meta": {
"title": "LTXV Preprocess"
}
},
"478:335": {
"class_type": "LTXVAudioVAELoader",
"inputs": {
"ckpt_name": "ltx-2.3-22b-dev-fp8.safetensors"
},
"_meta": {
"title": "Load LTXV Audio VAE"
}
},
"478:601": {
"class_type": "LoraLoaderModelOnly",
"inputs": {
"lora_name": "ltx-2.3-id-lora-talkvid-3k.safetensors",
"model": [
"478:293",
0
],
"strength_model": 1
},
"_meta": {
"title": "Load LoRA"
}
}
})
workflow.set_input("440", "image", client.assets.from_url("https://comfy.org/catalog-media/75f8228c-37f2-4dcd-bd25-b02f020d9a6a/33ce8181-b949-4c83-93ce-1613d967d887"))
# 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("479"):
output.to_file(output.name)
for output in job.get_outputs("445"):
output.to_file(output.name)Try an example
About this workflow
This ComfyUI tutorial workflow turns a single image into a short, lip‑synced UGC-style video with cloned or selected voice. It chains three stages: prompt creation from your uploaded image, speech synthesis with ElevenLabs, and video generation with LTX‑2.3. The Create Prompt group routes the image from LoadImage into GeminiNode to auto-generate two texts: a performance-ready speech script (with expression tags) and a scene description. RegexExtract parses the Gemini response to separate the “speech” and “scene” sections cleanly for downstream nodes.
For audio, you can pick a preset voice via ElevenLabsVoiceSelector or bring your own tone with ElevenLabsInstantVoiceClone, then synthesize the final narration in ElevenLabsTextToSpeech and optionally save it with SaveAudioMP3. The Create Video group feeds the scene description and the audio into the LTX‑2.3 video node (UUID: 98fb87e2-23b5-4ecb-aacc-365912414a12) to produce a talking-style video with accurate lip sync, previewed by PreviewAny and written with SaveVideo. This setup is practical for rapid UGC, product explainers, testimonials, or social posts because it automates script writing from an image and guarantees voice-to-lips alignment through ElevenLabs + LTX.
Questions & answers
Yes. Provide a valid Gemini API key in GeminiNode for prompt generation and an ElevenLabs API key in the ElevenLabs nodes for voice selection/cloning and TTS. Without Gemini, you can bypass that step by manually writing the speech and scene prompts.
The generated narration from ElevenLabsTextToSpeech is fed directly into the LTX‑2.3 video node, which uses the audio to align mouth movements. Keeping the final TTS file as the exact audio source ensures accurate lip sync.
Yes. Use LoadAudio to input your narration and connect it where the TTS audio would go in the Create Video group. Skip the ElevenLabsTextToSpeech step in that case.
GeminiNode returns both a speech script and a scene description. RegexExtract parses that response into two clean outputs—one for ElevenLabsTextToSpeech (speech prompt) and one for the LTX‑2.3 video node (scene prompt)—to keep the pipeline reliable.