Runware Sync 3
Make a video's mouth match any audio, without running a lip-sync model locally
- speech
- video
Sync 3 is Runware's lip-sync model, and this node is the entire thing in one box: feed it a video and some audio (or just a script), get back a video where the speaker's mouth actually tracks the words. That's a genuinely fiddly job to do locally - the best lip-sync models are heavy, the alignment math is unforgiving, and a bad sync is worse than no sync. On Runware it's a cloud call, which for a task like this is a fair trade: the video processing happens on their GPUs, your machine just shuffles media around.
The node ships with two ways to get the audio in, and the difference matters. You can connect an audio input as a UUID or URL (upload a clip, point at a hosted file, or reuse a Runware result URL), or you can wire a speech socket - the Runware/Params → Speech builder - which lets you type text and have it synthesized server-side before the sync happens. That second path is the one that makes this node feel like magic: type a script, pick a voice, and out comes the video talking.
How it works
It's the standard pack machinery. The node (sync:lipsync@3, videoInference) builds a request, sends it over REST through the runware-sdk, and when the job finishes downloads the result into a native VIDEO output. The SDK handles uploading your input video and audio so Runware's servers can see them. The settings block is where the actual lip-sync behavior lives, and it's worth knowing what each knob does:
- settings.mode - which region of the face gets animated (full face vs. just the mouth area, among a few options).
- settings.emotion - an emotional tone applied during re-animation, from a dropdown.
- settings.temperature - expressiveness of the lip movements; higher is livelier, lower is stiffer.
- settings.syncMode - what happens when audio and video lengths don't match. Default
cut_offtrims to the shorter one; other modes stretch or pad. - settings.tts.provider - defaults to
elevenlabs, withsimilarityBoostandstabilitysliders for voice control. This is the voice the Speech builder uses. - settings.activeSpeakerDetection.autoDetect - for multi-person video, automatically target whoever is speaking. Turn this on and you don't have to supply face coordinates.
- settings.occlusionDetection - handle faces that get partially covered during the clip.
The advanced_json field is the escape hatch: Runware documents that it accepts settings.activeSpeakerDetection.boundingBoxes, coordinates, and settings.segments as raw JSON, so if you want manual control over who's talking and when, that's where it goes.
The inputs that matter
The only required input is video (a UUID or URL string). For a first run, keep it simple:
- Wire your clip into
video. - Type your script into a Speech builder and connect it to
speech- or connect a pre-made audio file toaudioinstead. - Leave
settings.syncModeatcut_offand letsettings.temperaturesit around 0.5. - If there are multiple people in frame, flip
settings.activeSpeakerDetection.autoDetecton.
Output is a single video (VIDEO) - wire it into a video preview/save node. Output format defaults to MP4.
Install and API key
Shared install with the whole pack:
cd ComfyUI/custom_nodes
git clone https://github.com/Runware/ComfyUI-Runware
pip install -r ComfyUI-Runware/requirements.txt
Restart ComfyUI, or grab Runware from ComfyUI Manager. Needs an API key from runware.ai/api-keys - set it in ComfyUI Settings → Runware API key, via RUNWARE_API_KEY, or runware auth login.
Gotchas
The number one beginner trap here is feeding the node a local video file as if it were a path - the video input wants a UUID or URL, not a local filesystem path, so upload your clip first (Runware Upload Image is for images; for video you'll typically use the SDK upload or a hosted URL). Also remember that a speech-driven run means two cloud services are involved (TTS plus lip-sync), so the cost and latency are both higher than a plain video pass. And sync quality is content-dependent: long gaps with no dialogue are where syncMode and temperature earn their keep. If the mouth drifts, nudge temperature down and re-check your audio length.
Inputs (20)
| Name | Type | Default | Description |
|---|---|---|---|
| video | STRING | Video input (UUID or URL). | |
| speechopt | RUNWARE_SPEECH | — | |
| audioopt | STRING | Audio input (UUID or URL). | |
| numberResultsopt | INT | 11–4 | Number of results to generate. Each result uses a different seed, producing variations of the same parameters. |
| settings.activeSpeakerDetection.autoDetectopt | BOOLEAN | false | Automatically detect and target the active speaker. |
| settings.emotionopt | COMBO | (default) | Emotional tone for performance re-animation. |
| settings.activeSpeakerDetection.frameNumberopt | BOOLEAN | false | Enable to set settings.activeSpeakerDetection.frameNumber. Off uses the model's default. |
| settings.activeSpeakerDetection.frameNumber_valueopt | INT | 0 | Frame index corresponding to the provided face coordinates. |
| settings.modeopt | COMBO | (default) | Controls the region of facial animation applied during lip sync. |
| settings.occlusionDetectionopt | BOOLEAN | false | Enable occlusion handling for obstructed faces. |
| safetyopt | BOOLEAN | false | Enable to set safety. Off uses the model's default. |
| safety.checkContentopt | BOOLEAN | false | Enable or disable content safety checking. |
| safety.modeopt | COMBO | fast | Safety checking mode for video generation. |
| settings.syncModeopt | COMBO | cut_off | Synchronization strategy when audio and video durations don't match. |
| settings.temperatureopt | FLOAT | 0.500–1 | Expressiveness of lip sync and facial movements. |
| ttlopt | BOOLEAN | false | Enable to set ttl. Off uses the model's default. |
| ttl_valueopt | INT | 60 | Time-to-live (TTL) in seconds for generated content. Only applies when `outputType` is `URL`. |
| outputFormatopt | COMBO | MP4 | File format for the generated video. |
| outputQualityopt | INT | 9520–99 | Compression quality of the output. Higher values preserve quality but increase file size. |
| advanced_jsonopt | STRING | Optional JSON merged into the request. For: settings.activeSpeakerDetection.boundingBoxes, settings.activeSpeakerDetection.coordinates, settings.segments |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| video | VIDEO | — |