ZenID FaceSwap
InstantID rebuilt as a real swap instead of a guided generation
- control_net
- model
- clip
- vae
- image_source
- image_face
- mask
- MODEL
- positive
- negative
- latent
Calling this a "face swap" undersells it and oversells it at the same time. ApplyZenID isn't ReActor - it doesn't paste pixels over an existing face. It's InstantID, the January 2024 zero-shot identity adapter, refactored so that instead of guiding a whole scene toward your reference face it swaps the face in an existing photo. The difference matters: the rest of the image - body, background, lighting, expression - stays put, and only the identity changes.
How it actually works
InstantID's formula was always three parts: an InsightFace ArcFace embedding for identity, an image adapter that injects that embedding through cross-attention, and IdentityNet, a ControlNet-shaped module that conditions on facial keypoints so the face keeps its geometry. ApplyZenID keeps all three and adds a swap path.
Here's the trick: it treats the swap as inpainting. InsightFace (the antelopev2 model) finds the face in your source photo, builds a bounding-box mask blurred by blur_kernel (default 51), then encodes the photo through the VAE as an inpaint latent with that mask. The face you feed in becomes the ArcFace embedding driving the InstantID patch, and the keypoints come from the target photo itself. You get a model that denoises the masked region into your chosen face while leaving everything else alone.
One thing that catches people: ApplyZenID doesn't take a text prompt. It builds its own internally - a blank positive and a quality negative - because there's nothing you need to say; the prompt is the photo you're swapping into. That's why it hands you positive and negative conditioning outputs rather than asking for them.
The inputs that matter
Most of the plumbing comes from a standard SDXL checkpoint: model, clip, vae from CheckpointLoaderSimple, and control_net from a ControlNetLoader pointed at the InstantID ControlNetModel. Beyond that, you actually touch:
image_source- the photo you're swapping into.image_face- whose face goes on. Clear, forward-facing shots work best.instantid_file- dropdown of files inComfyUI/models/instantid/.insightface-CPU,CUDA,ROCM, orCoreML. CUDA if you have the VRAM for it.weight(0.8 default) - identity strength, shared by both the adapter and the ControlNet.blur_kernel- how soft the swap mask edge is; raise it if you see a hard seam.
Outputs are the patched MODEL, the generated positive/negative, and the inpaint latent. Wire all four into a KSampler and you're done - the README's ZenID_FaceSwap.json workflow is exactly that graph.
Installing it
It's one of the packs ComfyUI Manager can grab by searching "ComfyUI_ZenID", or the manual route:
cd ComfyUI/custom_nodes
git clone https://github.com/vuongminh1907/ComfyUI_ZenID
pip install -r ComfyUI_ZenID/requirements.txt
python ComfyUI_ZenID/downloadmodel.py
The install script pulls three things into your ComfyUI/models/ folder: the InstantID ip-adapter.bin into instantid/, the InstantID ControlNetModel into controlnet/, and the antelopev2 InsightFace weights into insightface/. It's all local - no API, no key, ever.
The gotchas nobody puts in the tutorial
First, the licensing, because it's the trap that actually bites. InstantID's code is Apache 2.0, but the released checkpoints are research-only, and the InsightFace face models are non-commercial. That means anything you make with this node is fine for personal fun and genuinely unsellable. ReActor-style commercial swap work isn't this node's lane.
Second, it's SDXL-only. InstantID never got a Flux port - that's a large part of why the ecosystem moved to PuLID - so this node expects an SDXL or Illustrious/Pony checkpoint. The README's "Flux support" update is a different pack (ComfyUI_InfiniteYou), not this node.
Third, the dependency install. insightface + onnxruntime is historically one of the more painful installs in the ecosystem; the pack pins onnxruntime-gpu only on x86_64 non-Windows, so your platform decides what you get. Keep ComfyUI updated first, as the README insists. And if you get "Reference Image: No face detected", the face is too small, tilted, or occluded for the detector - swap in a cleaner reference before touching sliders.
Worth knowing what you're running: this is a demo by the Vietnamese studio ZenAI, published partly to drive business to their paid face product. It works, and the swap quality is genuinely good for a free node - just know the README's star-warship politeness is a lead-gen funnel. That doesn't make the node worse, but it explains the missing documentation.
Inputs (13)
| Name | Type | Default | Description |
|---|---|---|---|
| control_net | CONTROL_NET | — | |
| model | MODEL | — | |
| clip | CLIP | — | |
| vae | VAE | — | |
| image_source | IMAGE | — | |
| image_face | IMAGE | — | |
| instantid_file | COMBO | 0 options: | |
| insightface | COMBO | 4 options: CPU, CUDA, ROCM, CoreML | |
| weight | FLOAT | 0.800–5 | — |
| start_at | FLOAT | 0.0000–1 | — |
| end_at | FLOAT | 1.0000–1 | — |
| blur_kernel | INT | 511–101 | — |
| maskopt | MASK | — |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| MODEL | MODEL | — |
| positive | CONDITIONING | — |
| negative | CONDITIONING | — |
| latent | LATENT | — |