BFS H3 Describe References (Generate Text per Picture)
Caption five references in one call and you get a collage hallucination
- clip
- 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
- reference_set
- references
- reference_inventory
- reference_images
Why you'd reach for it
Reference-to-video with MiniMax H3 means juggling pictures: this face, that jacket, this room, that art style. The model can take them all. The problem is the prompt - H3 wants a written description of what each picture contributes, and you're not going to type "<Picture 2> is a cream cable-knit sweater, cropped at the waist, oversized shoulders" five times a run.
So you put a vision model in the graph. The obvious move is to wire all five images into one captioner node and ask for a description. That's the move this node exists to stop.
Batch five images into one VLM call and you get a joint description: the model starts narrating the set as if it were one scene, borrowing the jacket from image 3 to dress the person in image 1. Then your prompt asserts things about a picture that aren't in it, and H3 dutifully renders them. BFS H3 Describe References calls Generate Text once per image, in isolation, and never builds a collage.
How it works
bfs_h3_native_workflow.py is refreshingly literal here. It collects however many ref_image_N sockets are connected, sorts them, and then loops: one TextGenerate.execute() call per image, with a prompt that says only this image is visible in this call, the other picture numbers in the user request refer to images the model cannot see, don't copy another picture's description, and don't treat background or a still pose as target-setting instructions.
Each call is deterministic - sampling mode off, thinking off, and MTP off where the ComfyUI build exposes that flag. You want a caption, not creativity. Any leading Answer: prefix is stripped, and an empty response raises a clear error rather than silently producing a blank picture description.
Before any of that, two validations fire. The slots must be consecutive starting at 1; gaps are rejected, because native H3 numbers attached images in insertion order and a hole would silently renumber Picture 3 into Picture 2. And every image must be a single image, not a batch - Picture 2 must be one image, not a video/batch, with the suggestion to use separate slots for multiple views.
Output is a labelled inventory: an Attached references: N (Picture 1 through Picture N) header, then <Picture 1>: … blocks. That header is load-bearing - the prompt builder downstream parses it to know how many images exist.
Inputs and outputs a beginner touches
clip- this is not H3's text encoder. It's the VLM that does the captioning, loaded through ComfyUI's text-generation CLIP loader (the example workflow uses a locally installed generation-capable Qwen3-VL 4B). Confusing the two is the single most common mistake with these nodes: the thing that writes your prompt and the thing that encodes it are different models.request- the same user request used everywhere else. It's passed into each per-image prompt as context, and it's checked here: if you ask for<Picture 2>but only one image is connected, you get an error listing the attached set before spending any VLM time.tokens_per_image(default 384) - the length budget per caption. Long, detailed references - a full outfit on a busy background - truncate here. Bump to 512–768 if captions keep ending mid-sentence.max_image_side(default 1024) - the captioner sees a downscaled copy. The original pixels still go to H3 untouched, so raising this costs captioning time, not generation quality.ref_image_1…ref_image_9- optional, consecutive.
Outputs: references, a BFS_H3_REFERENCE_SET that carries the original images forward (it reads as an opaque set - you don't need to inspect it), and reference_inventory, the STRING you feed to BFS H3 Build Native Prompt. Wire references into BFS H3 Native Reference Set to Video and make sure both nodes see the same set - that's the entire reason the set type exists.
Install
Manager → search ComfyUI-BFSNodes, or:
cd ComfyUI/custom_nodes
git clone https://github.com/alisson-anjos/ComfyUI-BFSNodes.git
cd ComfyUI-BFSNodes && pip install -r requirements.txt
Restart after. The Python side is light (torch, numpy, Pillow, safetensors, librosa, opencv-python, scenedetect); the real weight is whatever VLM you load for clip. This is BFSNodes 1.115.21+ territory, and it needs a ComfyUI new enough to ship the native Generate Text node.
Where people get burned
Gaps. Connect slots 1, 2 and 4 out of habit and the node stops you. It's annoying exactly once; the alternative is silent Picture-3-becomes-Picture-2 identity swaps that are genuinely hard to debug.
Captions that ignore your intent. Adding a fourth reference doesn't auto-describe the right thing for it. The request has to say what role each picture plays - identity, outfit, setting, style, object. Multiple images describing one person is fine and expected; a VLM will still guess wrong now and then, which is why you should read the inventory before spending a 5090-minute render on it.
Wrong CLIP. If Generate Text errors out immediately, check you didn't plug H3's text encoder into clip. The node needs a model that can look at an image and talk.
Inputs (14)
| Name | Type | Default | Description |
|---|---|---|---|
| clip | CLIP | — | |
| request | STRING | — | |
| tokens_per_image | INT | 38464–2048 | — |
| max_image_side | INT | 1024256–2048 | — |
| ref_image_1opt | IMAGE | — | |
| ref_image_2opt | IMAGE | — | |
| ref_image_3opt | IMAGE | — | |
| ref_image_4opt | IMAGE | — | |
| ref_image_5opt | IMAGE | — | |
| ref_image_6opt | IMAGE | — | |
| ref_image_7opt | IMAGE | — | |
| ref_image_8opt | IMAGE | — | |
| ref_image_9opt | IMAGE | — | |
| reference_setopt | BFS_H3_REFERENCE_SET | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| references | BFS_H3_REFERENCE_SET | — |
| reference_inventory | STRING | — |
| reference_images | IMAGE | — |