WithAnyone BBox Calculator
Telling WithAnyone where each face goes
- bbox_string
- debug_img
The hardest part of putting multiple specific people in one image isn't making them look like themselves - it's stopping the model from mixing their faces into an average blob. WithAnyone solves that with bounding boxes: each identity gets an invisible region of the frame, and its face only gets to live there. This node is how you draw those regions.
Everything about it is normalized, which takes a second to get used to but makes it resolution-independent. All four inputs are 0-to-1 fractions of the image rather than pixels.
- box_pos_x / box_pos_y - the center of the box, as a fraction of width / height. 0.5/0.5 is dead center.
- box_width / box_height - how big the box is, as a fraction of the image. 0.5 wide on a 1024px image means 512px.
The node converts that to the format the pipeline wants - x1,y1,x2,y2 in relative coordinates - and outputs two things:
- bbox_string - the box itself. Wire this into the
bboxinput of a WithAnyone Single Person Conditioning node. - debug_img - a 512px preview with the box drawn on it, so you can see what you just placed without running a full generation.
Why the bbox matters so much
Inside the sampler, each person's ArcFace and SigLIP embeddings are injected through per-person cross-attention masks built from those boxes. Identity A's embeddings only get to influence the region where A's face is supposed to be, and B's stay in B's. That spatial anchoring is exactly what regional-prompting-style approaches on SDXL had to bolt on externally; WithAnyone has it baked into the model, and the bbox is the dial you turn.
The flip side: the README is blunt that if you leave it to the model, the faces get placed randomly from a preset list - a fallback it calls "not recommended." Random placement means random composition. You'll be fighting the layout instead of designing it.
The gotchas, from the source
Two failure modes are baked into the code, and both bite beginners. First, bboxes are all-or-nothing: if you supply one for person A but not person B, the sampler throws "Either all persons must have bboxes or none should have bboxes." Decide up front - compose every person, or none. Second, the box must be a real box: if it's under 4px after scaling to the output resolution, you get "Invalid bbox for person (too small)." Keep faces reasonably sized; you're placing a face, not a dot.
Also worth knowing: values are relative fractions, so 0.5 width on a wide image isn't square. If you want a square face region, think in terms of both dimensions - a 0.5-by-0.5 box is only square on a square canvas.
Installation
This ships in the WithAnyone pack, so it comes with the rest of the nodes:
cd ComfyUI/custom_nodes
git clone https://github.com/okdalto/ComfyUI-WithAnyone
cd ComfyUI-WithAnyone
pip install -r requirements.txt
Or install "WithAnyone" via ComfyUI Manager and restart. The chain you're building toward is: BBox Calculator → Single Person Conditioning (per person) → Sampler. One box node per person you're placing, one per conditioning node.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| box_pos_x | FLOAT | 0.500–1 | — |
| box_pos_y | FLOAT | 0.500–1 | — |
| box_width | FLOAT | 0.500–1 | — |
| box_height | FLOAT | 0.500–1 | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| bbox_string | STRING | — |
| debug_img | IMAGE | — |