Magic Hour: Face Swap (Video)
Magic Hour: Face Swap (Video)
- video
- face_image
- video
- video_path
- credits_charged
Video face swap is the reason half the people searching for this pack are here. One photo of a face, one clip, and the face in the clip is now that face - for the whole duration, tracking through motion, at a quality that doesn't scream "masked and pasted".
The thing that trips everyone
The node hardcodes all-faces mode. Not a widget, not an option - the request always goes out in all-faces mode, which means every face in the frame gets swapped with your single face_image. Two people in the shot, both get your face. A crowd scene, you've made a crowd of clones.
That's a design choice on Magic Hour's side, and for the classic use case - one subject, one reference - it's exactly right. But it's the thing beginners don't expect, because locally you'd crop a face, swap it, and paste it back with a mask, so you'd naturally only touch the face you chose. Here there's no target selection. That's a real limitation, not something you can work around from inside the node.
If you need one specific face swapped out of several, your options are: pre-crop and reassemble the frames yourself, or go local.
How it works
Your VIDEO is written to a temp MP4, your face_image to a temp PNG, both uploaded. The job runs remotely, tracking the face through the clip and re-rendering it, then the finished MP4 is downloaded into ComfyUI/output/magic_hour/ and wrapped as a VIDEO.
version picks the engine generation: default, v2 or v1. Leaving it on default is the right move unless you have a reason - it lets Magic Hour route you to whatever they currently consider best. Reach for v1/v2 only to reproduce an older look or to dodge a regression in the current one. The node only sends a version when it isn't default, so the default path is genuinely "their choice, not yours".
Inputs and outputs
Required: video, face_image, version, start_seconds, end_seconds.
The window is the cost lever and it behaves consistently across this pack: end_seconds at 0 means to the end of the clip. If your source is long and you only need a beat, set both ends. Two minutes of footage is far more credits than twelve seconds of it, and the tracking quality doesn't improve with length.
Outputs: video (real VIDEO - Save Video, Video Combine, Preview all work), video_path (STRING with the saved file), credits_charged (INT).
Install
cd ComfyUI/custom_nodes
git clone https://github.com/magichourhq/comfyui-magic-hour
pip install -r comfyui-magic-hour/requirements.txt
Restart. The only dependency is the magic-hour SDK (>=0.87.1) - nothing to put in models/, no insightface model zoo to fetch, no onnxruntime install. That's the whole appeal compared with ReActor-style local swaps, which are fiddly to set up and then quality-bound by a single inswapper model. ComfyUI Manager users can search "Magic Hour" if cloning isn't your thing.
export MAGIC_HOUR_API_KEY=your_key_here
before launching, or a bare api_key.txt in the pack folder. Key never in a widget: widget values live inside the workflow JSON.
Where it fails, and what to do
Reference image quality sets the ceiling. A small, soft, three-quarter-angle reference gives you a soft, drifting swap. Front-on, well-lit, decently large, neutral expression. This is the single highest-leverage thing you control.
Fast motion and occlusion. Hands crossing the face, whip pans, someone turning away - tracking loses the face and you get warping for a few frames. Test the hardest part of your clip first.
The long wait. Video jobs take tens of seconds to minutes; the node waits synchronously and your queue is blocked. Don't batch five of these and assume you can keep working.
401 / 402 / 422. Bad key, no credits or plan limit, unsupported settings. The node surfaces Magic Hour's own message rather than a generic exception, which is genuinely helpful - read it before assuming the node is broken.
The economics. Magic Hour's consumer web product has a reputation for thrifty free tiers - small swap allowances, watermarks, slow queues - and that reputation gets attached to the API by association. They're different surfaces: the API is metered per call with no watermark, and credits_charged tells you exactly what each run took. Judge it on that number, not on the web app.
And the standing warning, from the node's own description: only swap faces you have the right to use. It's the oldest rule in this corner of the hobby and it's the one that gets people's accounts deleted.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| video | VIDEO | — | |
| face_image | IMAGE | — | |
| version | COMBO | default | 3 options: default, v2, v1 |
| start_seconds | FLOAT | 0.00–3600 | — |
| end_seconds | FLOAT | 0.00–3600 | 0 = to the end of the video |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| video | VIDEO | — |
| video_path | STRING | — |
| credits_charged | INT | — |