Mpi FLUX 3 References
Ten Reference Slots Without the Autogrow Dance
- clip
- vae
- negative
- ref_image_0
- ref_image_1
- ref_image_2
- ref_image_3
- ref_image_4
- ref_image_5
- ref_image_6
- ref_image_7
- ref_image_8
- ref_image_9
- positive
- negative
- empty_latent
- ref_tags
What this node is actually for
Reference conditioning, the modern way. Since Kontext taught people you can hand an image model a picture and a sentence instead of a LoRA, "put this face / this jacket / this font into the shot" became the default way to get consistency without training anything. Flux 2 pushed that to up to ten references in one context, and FLUX 3 Image Klein is where the line is now - so citing several references at once is normal, not exotic.
Which is exactly where ComfyUI's stock reference node gets awkward. Core's Set Reference Multi Latent grows its image inputs one at a time, an Autogrow slot per reference. Fine when you're hand-wiring at 2 a.m. Not fine for anything authored ahead of time: a saved workflow or a host app has no way to add a slot at inject time, and an empty slot wired from a loader that emits its "nothing here" stand-in gets encoded as a real reference. MpiFlux3References takes the boring route - all ten slots present all the time, and the empty ones thrown away.
Why ten flat slots is the whole trick
ref_image_0 through ref_image_9 are plain optional inputs, always there. Leave one unconnected and it's skipped. Connect a loader sitting on an empty path and it's also skipped - this pack's loaders emit a 1×1 image when block_if_empty is off, and the node treats a 1×1 as "nothing". A genuinely black reference reports its real size and goes through; it isn't guessing from content.
Then it renumbers the survivors and hands them to core's own ReferenceMultiLatent, so the tensor maths is ComfyUI's and not a private copy that rots when core changes. One graph covers every combination of one to ten references.
Write the prompt against slot numbers
This is the bit people trip over, and it's the point. You cite references by the slot you wired them into - <ref_image_0>, <ref_image_3> - not by the order the model ends up seeing them. The node knows both numberings, so it rewrites your tags to match what core presents and drops any tag pointing at an empty slot.
So with slots 2 and 5 wired and the rest empty:
Put the hat from <ref_image_5> on the man in <ref_image_2>, ignore <ref_image_0>.
becomes <ref_image_1> and <ref_image_0>, and the dead tag is deleted (the words around it stay - you get "ignore .", the least-bad option). Bare ref_image_N values in a layout table's "from" field move with the same renumbering; a bare empty one is left alone, because yanking it would break the JSON. The ref_tags output is the map it produced, as a string - wire it to a text display node the first few times.
The inputs you set, and the outputs
Required: clip and vae (the model's own text encoder and VAE - this node encodes the prompt and the references itself), prompt (multiline, and yes it accepts dynamic prompts), resolution (resize or keep resolution), and megapixels (target size per reference when resizing - 1.0 is roughly 1024×1024, aspect kept).
Leave resolution on resize and the references around 1 MP each. Ten full-resolution camera files is a lot of latent tokens for no gain, and it's the commonest way to turn a working graph into a VRAM apology.
Optional negative picks up the same reference latents. Wire nothing and the negative output just repeats the positive, same as core - which is why a negative prompt feels like it does nothing on reference runs.
Outputs: positive, negative, empty_latent, ref_tags. Wire the first two into your sampler. Watch the latent. empty_latent is core's - with references it's the first reference's latent times zero, and with no references it's a square 4-channel SD latent, which is wrong for FLUX 3. For a text-to-image pass, take your latent from core's EmptyFlux2LatentImage and let empty_latent be.
Install
The pack ships no requirements file and downloads no models - it runs on the torch/Pillow/scipy ComfyUI already installs. Easiest route is ComfyUI Manager: search ComfyUi-MpiNodes (publisher mad-pony-interactive, Comfy Registry). Manually:
cd ComfyUI/custom_nodes
git clone https://github.com/MadPonyInteractive/ComfyUi-MpiNodes
# then restart ComfyUI
You need the FLUX 3 weights plus their separate CLIP and VAE files, loaded by your usual loaders. Nothing is bundled.
Where it goes wrong
"ReferenceMultiLatent is missing - MpiFlux3References needs ComfyUI 0.41.0 or newer." The node refusing cleanly rather than half-working. Update ComfyUI; the core class it delegates to is the whole conditioning path.
The model ignores a reference you clearly wired. Check ref_tags. Either the slot was empty by the node's definition - a 1×1 sentinel from a loader sitting on a path that didn't load - or you cited a slot whose tag then got dropped. Reading the map takes ten seconds.
Everything is slow and you're near OOM. Ten references is ten encodes. Cut megapixels before you cut references - 0.5 MP still carries identity, and one graph handling "sometimes two references, sometimes seven" is the whole point of the node.
Inputs (16)
| Name | Type | Default | Description |
|---|---|---|---|
| clip | CLIP | — | |
| vae | VAE | — | |
| prompt | STRING | Cite references by SLOT: <ref_image_0> is whatever is wired into ref_image_0. The node rewrites tags to the order core presents once empty slots are dropped (see ref_tags); a tag naming an empty slot is removed. | |
| resolution | COMBO | resize | resize scales each reference to `megapixels`, aspect kept. keep resolution encodes it as it is. |
| megapixels | FLOAT | 1.000.01–16 | Target size of each reference when resolution is resize. 1.0 is about 1024 x 1024. |
| negativeopt | CONDITIONING | Optional. Gets the same reference latents. Unconnected, the positive is returned in its place, as core does. | |
| ref_image_0opt | IMAGE | Reference image, cited in the prompt as <ref_image_0>. Leave unconnected, or feed a loader with block_if_empty OFF, to skip it. | |
| ref_image_1opt | IMAGE | Reference image, cited in the prompt as <ref_image_1>. Leave unconnected, or feed a loader with block_if_empty OFF, to skip it. | |
| ref_image_2opt | IMAGE | Reference image, cited in the prompt as <ref_image_2>. Leave unconnected, or feed a loader with block_if_empty OFF, to skip it. | |
| ref_image_3opt | IMAGE | Reference image, cited in the prompt as <ref_image_3>. Leave unconnected, or feed a loader with block_if_empty OFF, to skip it. | |
| ref_image_4opt | IMAGE | Reference image, cited in the prompt as <ref_image_4>. Leave unconnected, or feed a loader with block_if_empty OFF, to skip it. | |
| ref_image_5opt | IMAGE | Reference image, cited in the prompt as <ref_image_5>. Leave unconnected, or feed a loader with block_if_empty OFF, to skip it. | |
| ref_image_6opt | IMAGE | Reference image, cited in the prompt as <ref_image_6>. Leave unconnected, or feed a loader with block_if_empty OFF, to skip it. | |
| ref_image_7opt | IMAGE | Reference image, cited in the prompt as <ref_image_7>. Leave unconnected, or feed a loader with block_if_empty OFF, to skip it. | |
| ref_image_8opt | IMAGE | Reference image, cited in the prompt as <ref_image_8>. Leave unconnected, or feed a loader with block_if_empty OFF, to skip it. | |
| ref_image_9opt | IMAGE | Reference image, cited in the prompt as <ref_image_9>. Leave unconnected, or feed a loader with block_if_empty OFF, to skip it. |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| positive | CONDITIONING | — |
| negative | CONDITIONING | — |
| empty_latent | LATENT | — |
| ref_tags | STRING | — |