Nodes/NKD Klein Tools/😺NKD Klein Box Prompt
ComfyUI Node

😺NKD Klein Box Prompt

Type your Klein 3 layout in six lines instead of drawing it

By Nekodificador·Created 6 months ago·Updated 2 days ago· 88
😺NKD Klein Box Prompt
    • prompt
    â—„captionâ–º
    â—„elementsâ–º

    Klein 3 can be told where things go, in the prompt, as a list of boxes. The pack's Presampling node gives you a canvas editor for drawing those boxes. This node is the other way in: you type them, in one widget. Two inputs, one string out, no clicking on images.

    That sounds like a downgrade until you want a layout you can copy between graphs, template with a few swapped numbers, or hand over without screenshotting a modal. Drawn boxes live inside a workflow's JSON and are painful to diff. A caption plus five lines of coordinates you can keep in a notes app.

    What it actually emits

    Not a magic format the node invented - it's BFL's documented element list for FLUX.3, appended to your caption as JSON. The model reads the caption as instructions, then matches the <id> tags in that text to boxes in the list. So the tagging isn't decoration.

    Two shapes come out, and which one you get depends on what you write:

    • No source or arrow anywhere and you get the layout shape, {"id", "bbox", "desc"} per element: compose something new with these things in these places.
    • One from N or -> and the whole prompt flips to the edit shape, {"id", "from", "src_bbox", "tgt_bbox", "desc"} - from is ref_image_N, tgt_bbox: null means remove, src_bbox == tgt_bbox means keep.

    Mixing a pure-layout line into an edit is allowed (-> [tgt], no source), and it comes out as new content on the edit - handy for "remove the dog and put a bench there" in one node.

    The language

    caption is a normal scene caption. elements is one line per element:

    apple_1 from 1: A glossy red apple. | [158,140,908,860] -> [300,380,700,720]
    cat_1 from 0: A tabby cat. | [265,40,855,400] -> remove
    base_1 from 0: The background. | [0,0,1000,1000] -> keep
    bird_1: A small bird. | -> [450,500,480,530]
    sky_1: A pale sky. | [0,0,400,1000]
    

    The shape is id [from N]: description | geometry. Boxes are [top, left, bottom, right] on a 0–1000 grid, always. Blank lines and # comments are ignored, so you can park alternatives instead of deleting them.

    Four things the parser is strict about, all of which surface as a red error naming the line number:

    • top < bottom and left < right. Getting the pair backwards is the standard mistake, because half the ecosystem writes [x1, y1, x2, y2].
    • Values inside 0–1000.
    • Ids unique within the node, starting with a letter or underscore - so apple_1, never 1_apple.
    • Every line needs geometry. A bare id: desc | is an error, not a no-op.

    The output prompt is a plain STRING for Presampling's positive; Presampling does the CLIP encoding, so this is pure text assembly. Leave elements empty and your caption comes back untouched - safe to keep in the graph while you're still writing.

    Why the caption matters more than the boxes

    Boxes guide placement and scale. They are not a mask - a box says roughly where and how big, and the model paints what it thinks belongs there. "Only these pixels change" is Presampling's mask input, not this.

    The caption is where the instruction lives, and with an LLM-encoded model that's the whole game - Qwen3-family encoders read your prompt as a message, not a bag of tags. Old habits like ((word:1.4)) are worse than useless here: ComfyUI weighting is disabled for these encoders and the parentheses get fed through as literal punctuation. Tag each element <id> next to what it is, and refer to references as <ref_image_0>, <ref_image_1>, counting from 0 - the same number as from N. Don't mix that with "the second image" in the same prompt; pick one register.

    Skip a tag and the node writes the sentence for you: "In <ref_image_0>, remove the tabby cat <cat_1> completely and fill its place with the surrounding background. Keep everything else in the image exactly unchanged." The author measured that against a bare "Remove…" and the bare version sometimes just didn't remove the thing, while this form plus a named filler ("empty sand") held up. Name what fills the hole and you get far fewer ghost-smudges where the object was.

    Install

    Manager → search NKD Klein Tools, or:

    cd ComfyUI/custom_nodes
    git clone https://github.com/Nekodificador/ComfyUI-NKD-Klein-Tools
    

    Restart ComfyUI. No pip steps - the requirements file only notes that torch and comfy_api ship with ComfyUI. You need PyTorch ≥ 2.0 and FLUX.3 Klein.

    Getting it wrong, in order of likelihood

    You're on Klein 2. Boxes are Klein 3 only, and Presampling logs "boxes only work with Klein 3 - ignored" while quietly discarding your beautiful layout. Reads like the node is broken; flip Presampling's Model setting to Klein 3.

    Your caption and lines disagree. <cat_1> in the text plus cat_1 in the line is what you want; cat in the text plus cat_1 in the line leaves an untagged element, and the auto-sentence may not say what you meant.

    You're expecting a mask. If the object shifted a little or new pixels appeared just outside its box, that's the model interpreting. And with a mask plus Use Detailing, boxes are remapped into the zoomed crop, anything landing outside it skipped with a log line - so keep thinking in whole-image coordinates.

    Or draw instead: the canvas editor on Presampling writes this same element list, and Box Detections builds one from a YOLO or SAM3 hit. All three land in the same place, so prototype by hand and freeze the good result into this node.

    Category😺NKD Nodes/Klein

    Inputs (2)

    NameTypeDefaultDescription
    captionSTRINGA full scene caption. Tag each element with <id> in the text. For edits, refer to references as <ref_image_0>, <ref_image_1>… counting from 0, the same number as 'from N' below. Any element you don't tag gets a sentence added for it, but your own words work better.
    elementsSTRINGOne element per line: id: desc | [t,l,b,r] — new element at a box id from 1: desc | [src] -> [tgt] — move (from = reference number) id from 0: desc | [src] -> remove — remove id from 0: desc | [src] -> keep — keep in place id: desc | -> [tgt] — no source: add something there, or change what is there (desc: 'Change the jacket to blue') To only name something the caption talks about, keep it. Lines starting with # are ignored.

    Outputs (1)

    NameTypeDescription
    promptSTRING—