MiniMax-H3 Identity Reference + RoPE (BFS)
A face channel the text encoder never sees
- model
- positive
- vae
- latent
- image
- model
- positive
- reference_preview
The setup, and the honest caveat
Most identity tools in this space attach a face to a prompt - an IP-Adapter embedding, a FaceID ArcFace vector, a PuLID module, some adapter or other grafted onto cross-attention. This node does something structurally different: it puts your portrait into the latent as a caption-only reference, at a control channel the LoRA was trained to expect, and deliberately keeps the picture away from the VLM/Qwen3-VL encoder. Your prompt describes what's happening; image carries what the person looks like.
That separation is the interesting idea. The catch is at the door: this requires a checkpoint trained with a matching identity reference role and control_latent_only: true. If your LoRA was trained only with a low-resolution guide, the author's own docs say the quiet part out loud - adding a portrait to it "is not a validated identity-preservation recipe". This is the experimental end of the pack. The two layouts and the source phase have CPU geometry and rotary parity tests, but no full pretrained H3 quality validation, so what's proven is the plumbing, not the face.
How it works
It takes your portrait, keeps its aspect, and resizes it down only to the trainer's /32 reference-image bucket - the guide's downscale_factor does not resize the portrait, which is a nice touch because it means the two references stay independent.
Then the interesting part. With reference_source_phase on, the node adds a fixed phase to H3's rotary angles: source_id * phase_scale * 10000**(-d/48). Text and targets stay source zero. In other words, every reference source gets its own position on the rotary axis, so the model can tell reference 1 from reference 2 from the text rather than having them collapse into one another. The patch is scoped, via a ModelPatcher object patch, to conditioning this node marked - other conditioning and other models keep their original rotary path, which is why chaining doesn't corrupt the rest of your graph.
The source_id values are what tie you to training: 1 for the guide, 2 for identity (the default here), matching training control channels 1 and 2. source_id also goes up to 99, because reference_source_phase is a general mechanism, not an identity-specific one.
Inputs and outputs
Required: model, positive, vae, latent (the same target latent your guide uses) and image (the portrait). Optional: guide_rope_layout (overlap/sidecar), reference_rope_layout (native/overlap/sidecar), reference_source_phase, reference_phase_scale, reference_sidecar_margin, source_id, downscale_factor (1–8, default 4) and lora_name.
lora_name is a metadata check only. It does not load LoRA weights - the author says this in the tooltip and in the guide, because everyone tries it. Load your LoRA normally and point this selector at the same file so the node can verify the role and mode the checkpoint was trained with.
Outputs: model, positive, and reference_preview - the actual resized pixels that went in. Look at it. It's the only way to confirm the portrait wasn't fitted in a way you didn't expect.
The chain
Guide first, then identity, both with model and conditioning threaded through:
LoRA loader ─ model ─► Downscaled Latent Guide ─ model ─► Identity Reference ─ model ─► sampler / scheduler
positive ──────────────► positive ──────────────► guider
Connect both final outputs and use the returned model for scheduling too. If you want a first experiment rather than an adventure, the author's suggestion is overlap layout plus phase, compared against the phase-off baseline. And keep the caption conventions consistent with training - the phase and the layout "distinguish sources; they do not guarantee the same face".
Install
cd ComfyUI/custom_nodes
git clone https://github.com/alisson-anjos/ComfyUI-BFSNodes
cd ComfyUI-BFSNodes && pip install -r requirements.txt
Needs a ComfyUI build with the native H3 nodes and the H3 VAE; the geometry helper (MiniMax-H3 Guide Target) is in the same pack. Restart after installing, and check the pack version - 1.47.0 is where this node and the RoPE options landed.
Who should skip it
Anyone without a checkpoint trained for this exact role, and anyone hoping it will rescue identity from an existing body-swap LoRA. Your money is better spent on a LoRA trained with control_latent_only: true and the reference role you actually want - this node is the inference half of a two-part contract, and half a contract doesn't preserve a face.
Inputs (13)
| Name | Type | Default | Description |
|---|---|---|---|
| model | MODEL | — | |
| positive | CONDITIONING | — | |
| vae | VAE | — | |
| latent | LATENT | — | |
| image | IMAGE | — | |
| guide_rope_layoutopt | COMBO | 2 options: overlap, sidecar | |
| reference_rope_layoutopt | COMBO | 3 options: native, overlap, sidecar | |
| reference_source_phaseopt | BOOLEAN | false | — |
| reference_phase_scaleopt | FLOAT | 1.00 | — |
| reference_sidecar_marginopt | FLOAT | 0.00 | — |
| source_idopt | INT | 21–99 | — |
| downscale_factoropt | INT | 41–8 | — |
| lora_nameopt | COMBO | 1 options: (manual) |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| model | MODEL | — |
| positive | CONDITIONING | — |
| reference_preview | IMAGE | — |