LTX 2.3 Cameraman IC-LoRA
Create camera-motion-driven videos with LTX 2.3 and the IC-LoRA Cameraman model.
About
This ComfyUI workflow turns a single still image into a video by transferring camera motion from a reference clip using the LTX 2.3 video model with the IC-LoRA Cameraman motion controller. The core generation happens in the LTX 2.3 node (custom generator node: 067cf66f-afc2-4672-8d26-481249ad5485) configured to preserve the visual content of your source image while applying the camera path extracted from the reference video. LoadImage provides the source frame, LoadVideo imports the motion reference, and GetVideoComponents reads the reference clip’s fps, duration, and dimensions so the render can match timing and playback characteristics.
The resulting frames are assembled with CreateVideo and written out via SaveVideo, with PreviewAny available for quick checks.
To streamline setup, the workflow is organized into groups: Load Image, Load Reference Video, Reference Video, LTX 2.3 IC-LoRA Cameraman, and Create IC-LoRA Cameraman Prompt. The optional GeminiNode in the prompt group helps you draft or document simple camera directions (e.g., "slow dolly-in, slight pan left") for reproducibility, while the actual motion is driven by the reference clip. ImageFromBatch ensures a single starting frame is passed into generation, and MarkdownNote provides in-canvas guidance.
The net result is cinematic camera motion—pan, tilt, orbit, dolly, or zoom—applied to your still without manually animating layers or keyframes.
What you provide
- Reading the workflow
Runs in ComfyUI. This workflow can't run on this page. Open it in Comfy Cloud with everything loaded, or download it for your own ComfyUI.
Try in CloudInside the workflow
A read-only preview of the node graph. Open it in Comfy Cloud or download it to edit the nodes.
Loading the graph
Open full-size workflow preview- Author
- ComfyUI
Requires custom nodes
- Reading the workflow
Use this workflow from code
Generated from the workflow graph and inputs when possible; stored examples are shown otherwise. Review credentials and endpoints before running code, which may incur charges.
What you need
- Runs on
- Comfy Cloud
- Key
- COMFY_API_KEY
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({
"666": {
"class_type": "LoadImage",
"inputs": {
"image": "bd2d530258429d526971cb5f3f90fa966b2984bc4770158288baffa527e9299f.png"
},
"_meta": {
"title": "Load Image"
}
},
"683": {
"class_type": "SaveVideo",
"inputs": {
"filename_prefix": "video/ComfyUI",
"format": "auto",
"format.codec": "auto",
"video": [
"5345:823",
0
]
},
"_meta": {
"title": "Save Video"
}
},
"753": {
"class_type": "GeminiNode",
"inputs": {
"images": [
"666",
0
],
"model": "gemini-3-1-flash-lite",
"prompt": "",
"seed": 452941233867679,
"system_prompt": "You are a prompt-only camera-motion transfer assistant.\n\nYou receive two inputs:\n1. an input image\n2. an input video\n\nYour task is to output one video-generation prompt in exactly this format:\n\n[VISUAL]: <scene and appearance description>\n[SPEECH]: <exact words the person should say>\n[SOUNDS]: <speaker vocal style + ambient/environmental sounds>\n\nDo not output anything before or after these three fields.\n\nThe input image is the visual source. The input video is the camera-motion source.\n\nThe prompt must focus on applying the camera motion from the video to the image while keeping the image content unchanged.\n\nThe [VISUAL] field must:\n- describe the camera motion transferred from the input video\n- state that the provided image remains visually unchanged\n- avoid detailed descriptions of the image’s subject, clothing, identity, setting, objects, lighting, colors, mood, or style\n- refer to the image only with neutral terms such as “the provided image,” “the original image,” “the image scene,” or “the original composition”\n- clearly state that only the camera motion is transferred from the video\n- clearly state that no visual elements from the video are transferred\n- include the camera movement’s direction, speed, angle, stabilization, framing change, zoom behavior, and any pan, tilt, dolly, orbit, crane, or handheld qualities when visible\n- describe sequential camera changes in chronological order if the video contains multiple movements\n- keep the image’s composition, subject appearance, lighting, colors, and style unchanged without describing them in detail\n\nThe [SPEECH] field must:\n- contain the exact spoken words only\n- be empty if the user has not provided speech\n- never invent dialogue\n- never describe speech in prose\n- never include quotation marks unless they are part of the spoken words\n\nThe [SOUNDS] field must:\n- describe speaker vocal style only if speech is provided or explicitly requested\n- describe ambient or environmental sounds only if the user asks for sound design\n- otherwise use “None”\n- never invent specific sounds from the image or video\n- never describe camera motion here\n\nDo not describe the input image in detail. Do not summarize the image. Do not describe the visual contents of the input video. Use the input video strictly to infer camera motion.\n\nDo not begin with broad setup phrases such as:\n- “Using the provided image as the visual source”\n- “Generate a video that preserves”\n- “Create a video based on”\n- “The image shows”\n\nDo not add subject actions, environmental movement, new objects, new people, visual effects, lighting changes, style changes, or narrative elements unless explicitly requested by the user.\n\nAnalyze the input video only for:\n- camera direction\n- camera path\n- camera speed\n- camera acceleration or deceleration\n- camera height\n- camera angle\n- pan\n- tilt\n- dolly\n- push-in\n- pull-back\n- truck left or right\n- crane movement\n- orbit movement\n- handheld drift\n- zoom behavior\n- stabilization style\n- parallax behavior\n- start framing\n- end framing\n- sequential movement changes\n\nOutput constraints:\n- output exactly three lines\n- line 1 must start with “[VISUAL]:”\n- line 2 must start with “[SPEECH]:”\n- line 3 must start with “[SOUNDS]:”\n- no markdown\n- no headings\n- no bullets\n- no explanations\n- no alternatives\n- no placeholders\n- no negative prompt section\n\nDefault output pattern:\n\n[VISUAL]: Apply only the camera motion from the reference video to the provided image: <precise camera movement description>. Keep the original image content, composition, lighting, colors, style, and subject appearance unchanged. Do not transfer any visual elements from the video; use it strictly as a camera-motion reference.\n[SPEECH]: \n[SOUNDS]: None\n\nDo not copy the default pattern mechanically. Replace the camera-motion portion with the actual motion observed in the input video.",
"video": [
"5352",
0
]
},
"_meta": {
"title": "Google Gemini"
}
},
"754": {
"class_type": "PreviewAny",
"inputs": {
"source": [
"753",
0
]
},
"_meta": {
"title": "Preview as Text"
}
},
"5346": {
"class_type": "GetVideoComponents",
"inputs": {
"video": [
"5347",
0
]
},
"_meta": {
"title": "Get Video Components"
}
},
"5347": {
"class_type": "LoadVideo",
"inputs": {
"file": "dce4353ac80a196bd417ceba52b2653c2f93d62aab87d6a216d4aea9afaa57b4.mp4"
},
"_meta": {
"title": "Load Video"
}
},
"5350": {
"class_type": "ImageFromBatch",
"inputs": {
"batch_index": 25,
"image": [
"5346",
0
],
"length": 50
},
"_meta": {
"title": "Get Image from Batch"
}
},
"5352": {
"class_type": "CreateVideo",
"inputs": {
"bit_depth": "auto",
"codec": "none",
"color_space": "sRGB",
"fps": [
"5346",
2
],
"images": [
"5350",
0
]
},
"_meta": {
"title": "Create Video"
}
},
"5345:293": {
"class_type": "LoraLoaderModelOnly",
"inputs": {
"lora_name": "ltx-2.3-22b-distilled-lora-384.safetensors",
"model": [
"5345:317",
0
],
"strength_model": 0.5
},
"_meta": {
"title": "Load LoRA"
}
},
"5345:313": {
"class_type": "LatentUpscaleModelLoader",
"inputs": {
"model_name": "ltx-2.3-spatial-upscaler-x2-1.1.safetensors"
},
"_meta": {
"title": "Load Latent Upscale Model"
}
},
"5345:317": {
"class_type": "CheckpointLoaderSimple",
"inputs": {
"ckpt_name": "ltx-2.3-22b-dev-fp8.safetensors"
},
"_meta": {
"title": "Load Checkpoint"
}
},
"5345: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"
}
},
"5345:335": {
"class_type": "LTXVAudioVAELoader",
"inputs": {
"ckpt_name": "ltx-2.3-22b-dev-fp8.safetensors"
},
"_meta": {
"title": "Load LTXV Audio VAE"
}
},
"5345:346": {
"class_type": "LoraLoaderModelOnly",
"inputs": {
"lora_name": "LTX2.3-22B_IC-LoRA-Cameraman_v1_10500.safetensors",
"model": [
"5345:293",
0
],
"strength_model": 1
},
"_meta": {
"title": "Load LoRA"
}
},
"5345:348": {
"class_type": "LTXVEmptyLatentAudio",
"inputs": {
"audio_vae": [
"5345:335",
0
],
"batch_size": 1,
"frame_rate": [
"5345:822",
1
],
"frames_number": [
"5345:827",
1
]
},
"_meta": {
"title": "Empty LTXV Latent Audio"
}
},
"5345:349": {
"class_type": "LTXVReferenceAudio",
"inputs": {
"audio_vae": [
"5345:335",
0
],
"end_percent": 1,
"identity_guidance_scale": 3,
"model": [
"5345:346",
0
],
"negative": [
"5345:812",
0
],
"positive": [
"5345:806",
0
],
"reference_audio": [
"5345:5357",
0
],
"start_percent": 0
},
"_meta": {
"title": "LTXV Reference Audio (ID-LoRA)"
}
},
"5345:5331": {
"class_type": "LTXVConcatAVLatent",
"inputs": {
"audio_latent": [
"5345:348",
0
],
"video_latent": [
"5345:5343",
2
]
},
"_meta": {
"title": "Concat AV Latent"
}
},
"5345:5342": {
"class_type": "LTXVImgToVideoConditionOnly",
"inputs": {
"bypass": False,
"image": [
"5345:5396",
0
],
"latent": [
"5345:826",
0
],
"strength": 1,
"vae": [
"5345:317",
2
]
},
"_meta": {
"title": "🅛🅣🅧 LTXV Img To Video Condition Only"
}
},
"5345:5343": {
"class_type": "LTXAddVideoICLoRAGuide",
"inputs": {
"crop": "disabled",
"frame_idx": 0,
"image": [
"5345:5348",
0
],
"latent": [
"5345:5342",
0
],
"latent_downscale_factor": 1,
"negative": [
"5345:807",
1
],
"positive": [
"5345:807",
0
],
"strength": 1,
"tile_frames": 97,
"tile_overlap": 64,
"tile_size": 256,
"use_streaming": False,
"use_tiled_encode": False,
"vae": [
"5345:317",
2
]
},
"_meta": {
"title": "🅛🅣🅧 Add Video IC-LoRA Guide"
}
},
"5345:5348": {
"class_type": "ImageFromBatch",
"inputs": {
"batch_index": 0,
"image": [
"5345:5355",
0
],
"length": [
"5345:827",
1
]
},
"_meta": {
"title": "Get Image from Batch"
}
},
"5345:5353": {
"class_type": "ResizeImageMaskNode",
"inputs": {
"input": [
"5346",
0
],
"resize_type": "scale dimensions",
"resize_type.crop": "center",
"resize_type.height": [
"5345:828",
0
],
"resize_type.width": [
"5345:816",
0
],
"scale_method": "lanczos"
},
"_meta": {
"title": "Resize Image/Mask"
}
},
"5345:5354": {
"class_type": "ResizeImagesByLongerEdge",
"inputs": {
"images": [
"5345:5353",
0
],
"longer_edge": 1536
},
"_meta": {
"title": "Resize Images by Longer Edge (DEPRECATED)"
}
},
"5345:5355": {
"class_type": "LTXVPreprocess",
"inputs": {
"image": [
"5345:5354",
0
],
"img_compression": 18
},
"_meta": {
"title": "LTXV Preprocess"
}
},
"5345:5357": {
"class_type": "TrimAudioDuration",
"inputs": {
"audio": [
"5346",
1
],
"duration": [
"5345:824",
0
],
"start_index": 0
},
"_meta": {
"title": "Trim Audio Duration"
}
},
"5345:5384": {
"class_type": "PreviewAny",
"inputs": {
"source": [
"5345:827",
1
]
},
"_meta": {
"title": "Preview as Text"
}
},
"5345:5396": {
"class_type": "ImageFromBatch",
"inputs": {
"batch_index": 0,
"image": [
"5345:819",
0
],
"length": [
"5345:827",
1
]
},
"_meta": {
"title": "Get Image from Batch"
}
},
"5345:794": {
"class_type": "RandomNoise",
"inputs": {
"noise_seed": 42
},
"_meta": {
"title": "RandomNoise"
}
},
"5345:795": {
"class_type": "RandomNoise",
"inputs": {
"noise_seed": 920645811960812
},
"_meta": {
"title": "RandomNoise"
}
},
"5345:796": {
"class_type": "LTXVConcatAVLatent",
"inputs": {
"audio_latent": [
"5345:809",
1
],
"video_latent": [
"5345:803",
0
]
},
"_meta": {
"title": "Concat AV Latent"
}
},
"5345:797": {
"class_type": "KSamplerSelect",
"inputs": {
"sampler_name": "euler_cfg_pp"
},
"_meta": {
"title": "KSamplerSelect"
}
},
"5345:798": {
"class_type": "ManualSigmas",
"inputs": {
"sigmas": "0.85, 0.7250, 0.4219, 0.0"
},
"_meta": {
"title": "ManualSigmas"
}
},
"5345:799": {
"class_type": "CFGGuider",
"inputs": {
"cfg": 1,
"model": [
"5345:293",
0
],
"negative": [
"5345:801",
1
],
"positive": [
"5345:801",
0
]
},
"_meta": {
"title": "CFG Guider"
}
},
"5345:800": {
"class_type": "SamplerCustomAdvanced",
"inputs": {
"guider": [
"5345:813",
0
],
"latent_image": [
"5345:5331",
0
],
"noise": [
"5345:795",
0
],
"sampler": [
"5345:804",
0
],
"sigmas": [
"5345:808",
0
]
},
"_meta": {
"title": "SamplerCustomAdvanced"
}
},
"5345:801": {
"class_type": "LTXVCropGuides",
"inputs": {
"latent": [
"5345:809",
0
],
"negative": [
"5345:5343",
1
],
"positive": [
"5345:5343",
0
]
},
"_meta": {
"title": "LTXVCropGuides"
}
},
"5345:802": {
"class_type": "LTXVLatentUpsampler",
"inputs": {
"samples": [
"5345:801",
2
],
"upscale_model": [
"5345:313",
0
],
"vae": [
"5345:317",
2
]
},
"_meta": {
"title": "LTXVLatentUpsampler"
}
},
"5345:803": {
"class_type": "LTXVImgToVideoInplace",
"inputs": {
"bypass": False,
"image": [
"5345:819",
0
],
"latent": [
"5345:802",
0
],
"strength": 1,
"vae": [
"5345:317",
2
]
},
"_meta": {
"title": "LTXVImgToVideoInplace"
}
},
"5345:804": {
"class_type": "KSamplerSelect",
"inputs": {
"sampler_name": "euler_ancestral_cfg_pp"
},
"_meta": {
"title": "KSamplerSelect"
}
},
"5345:805": {
"class_type": "LTXVAudioVAEDecode",
"inputs": {
"audio_vae": [
"5345:335",
0
],
"samples": [
"5345:811",
1
]
},
"_meta": {
"title": "LTXV Audio VAE Decode"
}
},
"5345:806": {
"class_type": "CLIPTextEncode",
"inputs": {
"clip": [
"5345:318",
0
],
"text": [
"5345:829",
0
]
},
"_meta": {
"title": "CLIP Text Encode (Prompt)"
}
},
"5345:807": {
"class_type": "LTXVConditioning",
"inputs": {
"frame_rate": [
"5345:822",
0
],
"negative": [
"5345:349",
2
],
"positive": [
"5345:349",
1
]
},
"_meta": {
"title": "LTXVConditioning"
}
},
"5345:808": {
"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"
}
},
"5345:809": {
"class_type": "LTXVSeparateAVLatent",
"inputs": {
"av_latent": [
"5345:800",
0
]
},
"_meta": {
"title": "Separate AV Latent"
}
},
"5345:810": {
"class_type": "SamplerCustomAdvanced",
"inputs": {
"guider": [
"5345:799",
0
],
"latent_image": [
"5345:796",
0
],
"noise": [
"5345:794",
0
],
"sampler": [
"5345:797",
0
],
"sigmas": [
"5345:798",
0
]
},
"_meta": {
"title": "SamplerCustomAdvanced"
}
},
"5345:811": {
"class_type": "LTXVSeparateAVLatent",
"inputs": {
"av_latent": [
"5345:810",
0
]
},
"_meta": {
"title": "Separate AV Latent"
}
},
"5345:812": {
"class_type": "CLIPTextEncode",
"inputs": {
"clip": [
"5345:318",
0
],
"text": "pc game, console game, video game, cartoon, childish, ugly"
},
"_meta": {
"title": "CLIP Text Encode (Prompt)"
}
},
"5345:813": {
"class_type": "CFGGuider",
"inputs": {
"cfg": 1,
"model": [
"5345:349",
0
],
"negative": [
"5345:5343",
1
],
"positive": [
"5345:5343",
0
]
},
"_meta": {
"title": "CFG Guider"
}
},
"5345:814": {
"class_type": "VAEDecodeTiled",
"inputs": {
"overlap": 64,
"samples": [
"5345:811",
0
],
"temporal_overlap": 4,
"temporal_size": 4096,
"tile_size": 768,
"vae": [
"5345:317",
2
]
},
"_meta": {
"title": "VAE Decode (Tiled)"
}
},
"5345:815": {
"class_type": "LTXVImgToVideoInplace",
"inputs": {
"bypass": False,
"image": [
"5345:819",
0
],
"latent": [
"5345:826",
0
],
"strength": 0.7,
"vae": [
"5345:317",
2
]
},
"_meta": {
"title": "LTXVImgToVideoInplace"
}
},
"5345:816": {
"class_type": "PrimitiveInt",
"inputs": {
"value": 1280
},
"_meta": {
"title": "Width"
}
},
"5345:817": {
"class_type": "SolidMask",
"inputs": {
"height": [
"5345:828",
0
],
"value": 0,
"width": [
"5345:816",
0
]
},
"_meta": {
"title": "Create Solid Mask"
}
},
"5345:818": {
"class_type": "ResizeImagesByLongerEdge",
"inputs": {
"images": [
"5345:830",
0
],
"longer_edge": 1536
},
"_meta": {
"title": "Resize Images by Longer Edge (DEPRECATED)"
}
},
"5345:819": {
"class_type": "LTXVPreprocess",
"inputs": {
"image": [
"5345:818",
0
],
"img_compression": 18
},
"_meta": {
"title": "LTXV Preprocess"
}
},
"5345:820": {
"class_type": "ComfyMathExpression",
"inputs": {
"expression": "a/2",
"values.a": [
"5345:828",
0
]
},
"_meta": {
"title": "Math Expression"
}
},
"5345:821": {
"class_type": "ComfyMathExpression",
"inputs": {
"expression": "a/2",
"values.a": [
"5345:816",
0
]
},
"_meta": {
"title": "Math Expression"
}
},
"5345:822": {
"class_type": "ComfyMathExpression",
"inputs": {
"expression": "a",
"values.a": [
"5345:825",
0
]
},
"_meta": {
"title": "Math Expression"
}
},
"5345:823": {
"class_type": "CreateVideo",
"inputs": {
"audio": [
"5345:805",
0
],
"bit_depth": "auto",
"codec": "none",
"color_space": "sRGB",
"fps": [
"5345:822",
0
],
"images": [
"5345:814",
0
]
},
"_meta": {
"title": "Create Video"
}
},
"5345:824": {
"class_type": "PrimitiveFloat",
"inputs": {
"value": 7
},
"_meta": {
"title": "Duration"
}
},
"5345:825": {
"class_type": "PrimitiveInt",
"inputs": {
"value": 25
},
"_meta": {
"title": "Frame Rate"
}
},
"5345:826": {
"class_type": "EmptyLTXVLatentVideo",
"inputs": {
"batch_size": 1,
"height": [
"5345:820",
1
],
"length": [
"5345:827",
1
],
"width": [
"5345:821",
1
]
},
"_meta": {
"title": "Empty LTXV Latent Video"
}
},
"5345:827": {
"class_type": "ComfyMathExpression",
"inputs": {
"expression": "a * b + 1",
"values.a": [
"5345:824",
0
],
"values.b": [
"5345:825",
0
]
},
"_meta": {
"title": "Math Expression"
}
},
"5345:828": {
"class_type": "PrimitiveInt",
"inputs": {
"value": 720
},
"_meta": {
"title": "Height"
}
},
"5345:829": {
"class_type": "PrimitiveStringMultiline",
"inputs": {
"value": [
"754",
0
]
},
"_meta": {
"title": "Prompt"
}
},
"5345:830": {
"class_type": "ResizeImageMaskNode",
"inputs": {
"input": [
"666",
0
],
"resize_type": "scale dimensions",
"resize_type.crop": "center",
"resize_type.height": [
"5345:828",
0
],
"resize_type.width": [
"5345:816",
0
],
"scale_method": "lanczos"
},
"_meta": {
"title": "Resize Image/Mask"
}
}
})
# run() also passes api_key to the partner nodes in this workflow.
job = client.run(workflow, api_key=api_key)Questions & answers
IC-LoRA Cameraman is a motion-control LoRA used with the LTX 2.3 video model. It guides the model to apply camera-style motion (pan, tilt, dolly, orbit, zoom) to your still while preserving the source image’s content.
LoadVideo plus GetVideoComponents set the timing (fps, duration) and provide the camera motion path. Your final video length and motion pacing follow the reference clip, so choose a clip with the exact move and speed you want.
Use a clean, stable reference with a single, smooth camera move; avoid excessive shake or abrupt cuts. Keep the source image’s aspect ratio close to the reference. If you see warping, try a shorter, steadier reference and moderate the move intensity.
Yes, but for best results, match your still image’s aspect to the reference video. GetVideoComponents passes fps and dimensions downstream; mismatches may cause cropping or padding depending on node settings. Align sizes to minimize artifacts.