LTX 2.3 Cameraman IC-LoRA
Create camera-motion-driven videos with LTX 2.3 and the IC-LoRA Cameraman model.
概要
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.
入力するもの
- ワークフローを読み込み中
ComfyUI で実行できます。 このワークフローはこのページでは実行できません。すべてが読み込まれた状態でComfy Cloudで開くか、ご自身のComfyUI用にダウンロードしてください。
Cloudで試すワークフローの内部
ノードグラフの読み取り専用プレビューです。ノードを編集するには、Comfy Cloudで開くかダウンロードしてください。
Loading the graph
Open full-size workflow preview- 作成者
- ComfyUI
カスタムノードが必要です
- ワークフローを読み込み中
コードからこのワークフローを使用する
可能な場合はワークフローグラフと入力から生成されます。それ以外の場合は保存された例が表示されます。料金が発生する場合があるため、コードを実行する前に認証情報とエンドポイントを確認してください。
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({
"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)よくある質問
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.