Nodes/NanoBanana FaceSwap/Nano Banana - Face Swap (Crop & Composite)
ComfyUI Node

Nano Banana - Face Swap (Crop & Composite)

The face swap that works when Nano Banana says no

By IxMxAMAR·Created 5 months ago·Updated 10 days ago· 4
Nano Banana - Face Swap (Crop & Composite)
  • target_image
  • identity_1
  • identity_2
  • identity_3
  • identity_4
  • identity_5
  • identity_6
  • network
  • image
  • status
  • mask
  • debug_sheet
◄api_key►
◄modelgemini-3.1-flash-image►
◄scopehead►
◄grid_modeseparate_refs►
◄custom_hint►
◄safety_thresholdBLOCK_NONE►
◄seed0►
◄batch_axistarget►
◄detectorauto►
◄crop_size1024►
◄histogram_matchfalse►
◄feather_px24►
◄image_size2K►
◄color_matchtrue►
◄grain_matchtrue►
◄sharpness_matchtrue►
◄composite_methodlaplacian►
◄match_directional_lightingfalse►
◄lab_strength0.60►
◄grain_strength1.00►
◄dry_runfalse►
◄timeout_ms180000►
◄ref_cap_px1024►
◄auto_relax_on_refusedfalse►

If you've spent an evening with Nano Banana 2's API, you know the pain: you send a target frame, a reference face, and a perfectly innocent edit request, and the model comes back with a safety refusal. Google tightened IMAGE_SAFETY and celebrity detection hard in January 2026, and the word "swap" itself is a tripwire for the classifier. This pack exists to route around that, and NanoBananaCropSwap is its escape hatch: detect the head, swap only that crop, composite it back into the frame.

The honest headline comes straight from the README, so let's get it out front: cropping fixes refusals caused by scene content - background, clothing, peripheral objects. It can actually increase refusals when the safety classifier is reacting to identity recognition itself. It is not a universal anti-refusal button. But when your whole-image attempt keeps dying on a busy background, this is the node you reach for.

How it works

Instead of sending the full frame, the node runs a head detector, crops tight, swaps the crop, and pastes it back with post-processing meant to make the seam vanish. The detector cascade is worth knowing: detector=auto tries MediaPipe (fast, CPU-only), then OpenCV YuNet (a ~1.8MB ONNX auto-downloaded to ~/.cache/nanobanana_faceswap/), then a Gemini vision bbox call as last resort. Pick one explicitly and it won't silently fall back - you get ERROR:detector_unavailable:<name> if it's missing.

The crop expansion is scope-driven: face grows the bbox 25% on each side, head goes up 60%, head+styling up 100%, so the crop carries just enough context. The fiddly part is the math, and the author deliberately does not clamp the expanded bbox to image bounds before squaring - clamping would shift the face center and make the swapped head land misaligned with the body. Out-of-bounds pixels are filled with cv2.copyMakeBorder replicate and composited back at the (possibly negative) anchor. That's the difference between "looks pasted" and "looks native."

Inputs that matter

  • api_key / model - your Gemini key (or set GEMINI_API_KEY in the environment), and which model: gemini-3.1-flash-image-preview (fast, default), gemini-3-pro-image-preview (Pro, slower, higher-res), or gemini-2.5-flash-image.
  • target_image + identity_1..identity_6 - the frame to change and up to six reference faces.
  • scope - face (eyes/nose/mouth/skin, keep hair and ears), head (adds hair + ears), head+styling (adds jewelry and makeup).
  • detector, crop_size (default 1024), feather_px (24) - the mechanics above.
  • composite_method (laplacian default) plus color_match, grain_match, sharpness_match - the integration stack that makes the result blend. match_directional_lighting is same-character mode only; leave it off for cross-identity swaps or it imposes the original person's skull geometry on the new identity.
  • dry_run skips the API call and shows you the prompt that would be sent - free debugging. ref_cap_px (1024) downscales refs, which saves bandwidth and actually improves identity transfer; 4K skin-pore detail confuses the model.

Outputs

The four outputs are (IMAGE, STRING, MASK, IMAGE): image (the result, or a red-tinted placeholder on refusal), status (OK / REFUSED:<category> / ERROR:<reason>), mask (the feathered alpha composite in full-image coordinates - handy if you want to inspect or reuse it), and debug_sheet - a 2x2 montage of tight bbox, expanded bbox, the square sent, and the square returned. That last one is your friend when something looks off; it tells you instantly whether the model or the compositing is to blame.

Install

Same as the rest of the pack - it's in ComfyUI Manager if you search "NanoBanana FaceSwap," or manually:

cd ComfyUI/custom_nodes
git clone https://github.com/IxMxAMAR/ComfyUI-NanoBanana-FaceSwap
cd ComfyUI-NanoBanana-FaceSwap
pip install -r requirements.txt

Restart ComfyUI. Heavy dependency alert: this is an API node, not a local model, so there's no big checkpoint to download - but mediapipe is required for the auto cascade, so run the requirements even if you only want one node. You'll need a Gemini API key either in the env or pasted into api_key.

Troubleshooting

  • REFUSED:<category> - red output means the classifier fired. That's the node working as designed; try auto_relax_on_refused or fall back to the pack's mask-inpaint or obfuscation pathways.
  • Face landing misaligned - almost always the crop math, but only if you've been in the source. In practice, re-check scope: face keeps hair, so if you wanted a full head replacement and only swapped the face, it'll look wrong.
  • ERROR:detector_unavailable:mediapipe - you skipped the requirements install.

Batch tip: batch_axis lets you iterate frames (target) or identities against a fixed target, and each iteration is its own API call with its own retry - one refusal doesn't kill the batch.

CategoryNanoBanana FaceSwap

Inputs (32)

NameTypeDefaultDescription
api_keySTRING—
modelCOMBOgemini-3.1-flash-image3 options: gemini-3.1-flash-image, gemini-3-pro-image, gemini-3.1-flash-lite-image
target_imageIMAGE—
identity_1IMAGE—
scopeCOMBOhead3 options: face, head, head+styling
grid_modeCOMBOseparate_refs2 options: separate_refs, auto_sheet
custom_hintSTRING—
safety_thresholdCOMBOBLOCK_NONE4 options: BLOCK_NONE, BLOCK_ONLY_HIGH, BLOCK_MEDIUM_AND_ABOVE, BLOCK_LOW_AND_ABOVE
seedINT00–2147483647—
batch_axisCOMBOtarget2 options: target, identity
detectorCOMBOauto4 options: auto, mediapipe, opencv_yunet, gemini_bbox
crop_sizeINT1024256–2048—
histogram_matchBOOLEANfalseLegacy RGB histogram match. Superseded by color_match below; keep off unless debugging.
feather_pxINT240–256—
image_sizeCOMBO2K4 options: 512, 1K, 2K, 4K
color_matchBOOLEANtrueLAB color match with face-polygon stats. Fixes skin-tone cast.
grain_matchBOOLEANtrueAdd synthetic Gaussian noise matching the target's cheek variance.
sharpness_matchBOOLEANtrueBlur Gemini's face if it's sharper than the body. Never sharpens.
composite_methodCOMBOlaplacian2 options: laplacian, feather
match_directional_lightingBOOLEANfalseSame-character mode. Imposes the original face's lighting envelope on the new face. Corrupts identity for cross-identity swaps.
lab_strengthFLOAT0.600–1—
grain_strengthFLOAT1.000–2—
identity_2optIMAGE—
identity_3optIMAGE—
identity_4optIMAGE—
identity_5optIMAGE—
identity_6optIMAGE—
dry_runoptBOOLEANfalseSkip API call; return a structured preview of the prompt + parts.
timeout_msoptINT1800005000–600000—
ref_cap_pxoptINT10240–4096Downscale ref longest-edge to this px. 0=off.
auto_relax_on_refusedoptBOOLEANfalseOn refusal, retry once with no safety_settings.
networkoptNB_NETWORKOptional. Wire a NanoBanana - Network Route node here to route this swap's API call through that proxy (e.g. US egress).

Outputs (4)

NameTypeDescription
imageIMAGE—
statusSTRING—
maskMASK—
debug_sheetIMAGE—