ChromapplyPulidFlux
The node that makes Chroma remember a face
- model
- pulid_flux
- eva_clip
- face_analysis
- image
- attn_mask
- prior_image
- MODEL
PuLID on Chroma used to just crash. Point the stock PuLID-Flux apply node at a Chroma checkpoint and you'd get 'Chroma' object has no attribute 'time_in' - a hard error that felt absurd, because Chroma is a Flux Schnell derivative and the adapter should have just worked. This node is the fix. ChromapplyPulidFlux is the apply step of nazgut/ComfyUI-PuLID-Flux-Chroma, a fork that teaches PuLID to patch its identity attention into Chroma's modified Flux transformer (and still does plain FLUX). One reference photo in, a face that survives across outfits, poses, and scenes - no LoRA, no training.
It's the payoff node in a four-node pipeline. The three loaders - ChromaPulidFluxModelLoader (PuLID weights), ChromaPulidFluxInsightFaceLoader (face detector + ArcFace embedder), and ChromaPulidFluxEvaClipLoader (the CLIP visual encoder) - each hand this node an object. You plug in a reference image, and the MODEL that comes out the bottom is your diffusion model with identity conditioning baked in. Wire that to a KSampler and you're done.
How it works
Under the hood it runs the same routine as the original balazik node: detect the largest face in your image, pull an InsightFace ArcFace embedding, crop and align the face, mask off the background with a BiSeNet face-parsing model, run the cleaned face through EVA-CLIP, concatenate the two feature vectors, and feed them into PuLID's ID encoder to produce the conditioning tokens. That's the "contrastive alignment" part - identity gets injected in a way that barely perturbs what the base model would otherwise draw.
The Chroma-specific magic is the last mile. It checks the loaded diffusion model for a distilled_guidance_layer; Chroma has one, standard FLUX doesn't. If found, it swaps in a Chroma-aware forward pass that builds Chroma's custom modulation vectors and handles the missing time_in attribute that broke every stock node. That's the whole reason this pack exists.
The inputs that actually matter
- model, pulid_flux, eva_clip, face_analysis - the three loader outputs plus your base model. All required.
- image - your reference face. Better quality = better likeness; the author's own tested sweet spot is around 1512x1512, not 4K+.
- weight (
-1to5, default1) - the strength dial. For the v0.9.1 PuLID model, 0.9–1.0 is the band people land in; crank past that and the face starts to distort. - start_at / end_at (
0to1) - the portion of the denoising schedule where PuLID is active. 0 to 1 means everywhere; tightening the window can fix late-stage face weirdness. - fusion - how multiple reference images combine:
meanis the safe official default,max/max_tokenemphasize dominant features (and can go uncanny),train_weightruns a fast embedding self-training pass. - use_gray (default on) - the official gray preprocessing. Turn it off for RGB if gray costs you detail.
The only output is a patched MODEL, which is why you can chain several of these nodes - each one registers its own identity under a unique ID, so two faces at different sigma windows works fine.
Install and models
ComfyUI Manager works if you search the pack title "PuLID Flux Chroma" (people have genuinely struggled to find it by node name - search the repo, not the node). Or manually:
cd ComfyUI/custom_nodes
git clone https://github.com/nazgut/ComfyUI-PuLID-Flux-Chroma
cd ComfyUI-PuLID-Flux-Chroma
pip install -r requirements.txt
Then restart ComfyUI. You still need pulid_flux_v0.9.1.safetensors (1.06 GB) in ComfyUI/models/pulid/, and the InsightFace antelopev2 model pack hand-placed in ComfyUI/models/insightface/. EVA-CLIP downloads itself on first run. The author tests on an AMD RX 6900 XT with fp8 Chroma, and the node quietly converts float8 models to bfloat16 internally so they don't blow up.
Common issues
- No face detected - it logs a warning and passes your model through unmodified, so you get a face-free image instead of a crash. Feed it a clearer, properly lit reference.
- Face too strong / melting - drop
weighttoward 0.8, or narrowstart_at/end_atso identity only lands in the middle of sampling. - Still getting
time_inerrors - that means you're actually running the older balazik node, not this pack. Make sure the Chroma fork is the one in your graph.
One honest caveat: PuLID gets the skeleton and skin tone right and stops there. For exact likeness, pair it with a pixel-level swap pass afterward. But for "same person, totally different scene" with zero training, this is the tool.
Inputs (15)
| Name | Type | Default | Description |
|---|---|---|---|
| model | MODEL | — | |
| pulid_flux | PULIDFLUX | — | |
| eva_clip | EVA_CLIP | — | |
| face_analysis | FACEANALYSIS | — | |
| image | IMAGE | — | |
| weight | FLOAT | 1.00-1–5 | — |
| start_at | FLOAT | 0.0000–1 | — |
| end_at | FLOAT | 1.0000–1 | — |
| fusion | COMBO | 7 options: mean, concat, max, norm_id, max_token, auto_weight, +1 | |
| fusion_weight_max | FLOAT | 1.00–20 | — |
| fusion_weight_min | FLOAT | 0.00–20 | — |
| train_step | INT | 10000–20000 | — |
| use_gray | BOOLEAN | true | — |
| attn_maskopt | MASK | — | |
| prior_imageopt | IMAGE | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| MODEL | MODEL | — |