Nodes/4A Bounding Boxes/Create Bounding Boxes
ComfyUI Node

Create Bounding Boxes

The box-drawing node that works without Node 2.0

By tsukino4a·Created 22 days ago·Updated 22 days ago· 3
Create Bounding Boxes
  • editor_state
  • background
  • bboxes
  • last_incoming
  • preview
  • bboxes
  • elements
◄width1024►
◄height1024►

What this actually is (and why it exists)

ComfyUI added a built-in Create Bounding Boxes node for drawing layout boxes on a canvas. Nice idea, annoying catch: its editor is a Vue widget, so it only renders with the new Node 2.0 frontend on. On the classic LiteGraph renderer most of us still run, you get nothing usable.

This pack is a straight port of that node - same backend logic, same outputs - with the editor re-implemented as a plain DOM widget under web/bounding_boxes.js. So you draw boxes exactly like the official node, without migrating your whole ComfyUI to a beta renderer. The credit line says it plainly: logic adapted from ComfyUI and ComfyUI_frontend, both GPL-3.0, so this is GPL-3.0 too.

Why you'd want boxes at all: this is the layout-control end of regional prompting. The old answer - mask the cross-attention per region - leaks, produces collage seams, and needs re-implementing on every architecture change. Putting boxes in the prompt instead is the newer, stabler route. Ideogram 4 made it normal: boxes are part of the JSON caption schema it was trained on, [y_min, x_min, y_max, x_max], may overlap and nest, and each can carry its own hex palette. Design-tool control instead of hoping a prompt lands.

How it works, mechanically

The canvas is a custom widget on editor_state. Boxes are stored as pixel coordinates against your width/height grid. On execute, the Python side converts each box to fractions of the canvas, builds the outputs from that, and renders a preview with PIL: a numbered tag per box (01, 02…), up to five palette swatches as a colour bar, your description text wrapped inside the rectangle. A background image is resized to a 1024 long edge and dimmed to 25% brightness so the overlay reads.

Two output shapes to know before you wire anything:

  • bboxes comes out as a frame of pixel boxes: x, y, width, height. That's what typed BOUNDING_BOX consumers expect.
  • elements comes out on a 0–1000 normalised grid as [ymin, xmin, ymax, xmax], with type (obj or text), text for text elements, desc, and up to five uppercase hex colours in color_palette. That's the Ideogram-style element shape, and the README points it straight at MOSS-Image Layout Prompt.

The inputs and outputs that matter

You set three required things. width and height default to 1024 and step in 16s - match them to the model you're prompting (Ideogram 4 wants multiples of 16 between 256 and 2048), since every box you draw gets scaled into that pixel grid. editor_state isn't a value you type; it's the canvas itself.

Optional background takes an IMAGE, so you can trace boxes over an existing render. Optional bboxes takes BOUNDING_BOX, ARRAY, or a STRING of JSON, and eats either pixel boxes or element dicts with a bbox, converting between them for you. That's the input that makes this composable: round-trip boxes out of a detector or an LLM, drop them in, nudge them by hand.

last_incoming is internal bookkeeping. Don't wire it.

Outputs: preview (IMAGE), bboxes (BOUNDING_BOX), elements (ARRAY). The node is an output node, so the preview also shows up right on the canvas. Wire preview to a Save Image node if you want the annotated layout as a file, elements to a prompt-builder or MOSS layout node, bboxes to anything that crops, pastes, or conditions on a rectangle.

Install

No pip dependencies - pyproject.toml declares dependencies = [], and there's no requirements.txt, so nothing to break in your env. Search 4A Bounding Boxes in ComfyUI Manager, or:

cd ComfyUI/custom_nodes
git clone https://github.com/tsukino4a/ComfyUI-4A-BoundingBoxes

Restart ComfyUI, then hard-refresh the browser (Ctrl+Shift+R / Cmd+Shift+R). The node's whole UI is JavaScript, and a cached frontend bundle is exactly how you end up with a node that has no canvas. It lands under the 4A-BoundingBoxes category.

Where people get burned

The precedence rule is the real trap, and the tooltip states it: a new upstream bboxes value re-initialises the canvas, and your on-canvas edits win until that upstream value changes again. So if something upstream is re-emitting boxes each run, your hand-tuning gets silently overwritten mid-session. Feed boxes in, edit, then disconnect the input if you care about the edits.

Draw in paint order - background element first, foreground last. The author put that in the tooltip for a reason, and it's the difference between an element list that reads sensibly and one that fights the model.

Empty canvas, empty bboxes list - check the preview before you blame the next node. A malformed JSON string on the bboxes input raises out loud instead of guessing, which is the right call.

One honest caveat on the pack: it's one author, small, and a fairly literal port. That's fine - you're relying on upstream ComfyUI's behaviour, not a novel invention - but don't expect a big issue tracker if the Vue and classic paths ever drift apart.

Category4A-BoundingBoxes

Inputs (6)

NameTypeDefaultDescription
widthINT102464–16384Width of the canvas and the pixel grid for the bounding boxes.
heightINT102464–16384Height of the canvas and the pixel grid for the bounding boxes.
editor_stateBBOX4A_EDITORDraw bounding boxes and set each box type, text, description, color palette. Start with background element first and foreground last.
backgroundoptIMAGEOptional image used as background in the canvas and preview.
bboxesoptBOUNDING_BOX,ARRAY,STRINGBounding boxes, elements, or a JSON string to initialize the canvas. A new upstream value initializes the canvas; edits made on the canvas take priority and are kept until the upstream value changes again.
last_incomingoptBBOX4A_STATEInternal state managed by the canvas: the upstream bboxes value that last initialized it.

Outputs (3)

NameTypeDescription
previewIMAGE—
bboxesBOUNDING_BOX—
elementsARRAY—