LC Bounding Boxes 🔲
Draw where things go instead of describing it
- image
- bboxes
- preview
- bboxes
- elements
Why you'd reach for it
FLUX 3 responds to layout the way Ideogram 4 does: say "a red cup on the left, a hat on the right" and you're rolling dice, but hand the model a table of boxes with names and it puts things where the table says. The catch is that "handing it a table" means writing coordinates. Every guide to bounding-box prompting eventually asks you to type [312, 40, 780, 290] into a JSON blob and hope your y-axis isn't flipped.
LC Bounding Boxes is the escape hatch from that. Connect a picture, draw rectangles on it with your mouse, type a name and a description under each one, and the node emits the same BOUNDING_BOX and elements array that ComfyUI's own Create Bounding Boxes produces. Same plumbing, wildly better ergonomics - the KB's node-plumbing notes make the point that a BOUNDING_BOX is just a rectangle with four numbers in it, and the entire difficulty is producing those numbers without arithmetic.
It's aimed at FLUX 3, but the outputs are the community shape, so anything that eats boxes will take them.
How it works
The canvas you drag on is a DOM element, not a LiteGraph drawing - that's deliberate, and it's why this node renders in ComfyUI's newer Nodes 2.0 frontend as well as the classic canvas. Your drawing is stored as JSON in the boxes widget: fractions of the image (0–1), plus a name, a description, a kind, and for move boxes a second target rectangle.
On run, the Python side parses that, drops anything with zero width or height, and converts each box to the convention Black Forest Labs uses in its prompt-upsampler instructions: [y_min, x_min, y_max, x_max] on a 0–1000 grid, top-left origin. Names get sanitised into ids - lowercased, non-alphanumerics to underscores, numbered per class - so a box called "red hat" becomes red_hat_1, a second one red_hat_2, and a text block becomes En_Text_1.
Each box carries one of four kinds: new, change, move (which uses its second rectangle as the destination) or remove. That kind is what decides whether the layout prompt builder downstream produces a fresh-picture table or an edit table, so it's not decoration.
The inputs that matter
- image (optional) - the picture to draw on. Its size sets the box grid, and for an edit this should be
ref_image_0, the image you're editing. No image, and the canvas iswidth×heightinstead. - width / height - fallback canvas size only. The moment you connect an image these are ignored, which surprises people who typed 1536 and got boxes on a 1024 grid.
- bboxes (optional) - incoming boxes from Create Bounding Boxes or a layout JSON, to start the canvas pre-populated. Boxes you draw here win until the incoming boxes change.
- boxes - the canvas state. Don't hand-edit this unless you enjoy debugging JSON.
What comes out
Three outputs, and they do different jobs. preview is an IMAGE: your picture dimmed and scaled to 1024 on the long edge, boxes drawn on. A reference render, not a deliverable. bboxes is the pixel-coordinate BOUNDING_BOX list, drop-in compatible with core boxes. elements is the one you actually want for FLUX 3: each box with its id, type, description, source and target box, and its kind - the input that LC Flux 3 Layout Prompt (or core's FLUX.3 Layout Prompt) turns into model-facing text.
It's an output node, so it runs and writes its background frame to temp whether or not you connect the preview - that's how the canvas shows your picture without a round trip.
Install
ComfyUI Manager, search ComfyUI LC123 Nodes, or by hand:
cd ComfyUI/custom_nodes
git clone https://github.com/lonecatone23/ComfyUI_LC123_nodes
No pip step. The repo ships no requirements.txt and no install script; it only needs the torch and numpy ComfyUI already has. Restart ComfyUI, look for [LC123] + lines in the console, then Ctrl+F5 to force the browser to pick up the new JS (the canvas widget lives in it).
You'll also need a FLUX 3 model and its text encoder, which this pack doesn't supply.
Common issues
Nothing appears where you drew it. Check a connected image didn't silently resize the grid - the canvas follows the picture, not width/height. Redraw after connecting.
Incoming boxes "reset" your edits. That's the documented behaviour: drawn boxes win until the upstream boxes change, then upstream wins. If you're iterating by hand, disconnect bboxes while you draw.
Your names look mangled. Spaces and punctuation become underscores and get an index appended - that's the id format the model expects. Pick names that survive it (hat, not the hat 🎩).
Nothing generates. This node builds boxes, not pixels. The chain is boxes → layout prompt → CLIP encode → a sampler fed by LC Flux 3 Reference Latent.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| width | INT | 102464–16384 | Canvas width when no picture is connected (a connected picture sets the size). |
| height | INT | 102464–16384 | Canvas height when no picture is connected. |
| boxes | STRING | [] | The boxes drawn on the canvas (kept by the node). |
| imageopt | IMAGE | The picture to draw on. Its size sets the box grid. For an edit, this is ref_image_0. | |
| bboxesopt | BOUNDING_BOX,ARRAY,STRING | Boxes from another node (Create Bounding Boxes, a layout JSON) to start the canvas with. Boxes drawn here win until the incoming boxes change. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| preview | IMAGE | The picture with the boxes drawn on it. |
| bboxes | BOUNDING_BOX | Pixel boxes (the same as Create Bounding Boxes). |
| elements | ARRAY | The boxes with their names, descriptions and what happens to them, for LC Flux 3 Layout Prompt (or FLUX.3 Layout Prompt). |