JB · Stitcher
Merge outfit, face, and location JSON under one character title — the assembler node
- raw_json
- string
The JB suite's trick is that each piece is built separately and combined at the end. You build a face in JB · Builder, an outfit in JB · Outfit Block, a location in JB · Location Block - and Stitcher is the node that wraps them all under a single title and deep-merges them into one JSON document. Set title to character_1, wire in the three fragment strings, and out comes character_1: {outfit: {...}, face: {...}, location: {...}} as a single string for your text encoder.
It's the multi-character assembler, too: chain a second Stitcher with title set to character_2 and feed both outputs into a parent Stitcher, and you've got two distinct named character blocks that won't bleed into each other - which is exactly the "two characters, same image" problem from every character-consistency guide, solved with structure instead of hoping the model keeps them apart.
How the merge actually behaves
Each input is parsed as JSON, then merged under the title with rules worth knowing:
- Same-level scalar leaves: last input wins. If input_1 sets
outfit: silkand input_2 also setsoutfit: cotton, cotton wins. - Sub-fields merge recursively. A
face: {hair: brown}from input_1 and aface: {eyes: blue}from input_2 combine intoface: {hair: brown, eyes: blue}- nothing gets clobbered. - Arrays append. If two inputs both define a list, the lists concatenate.
- Bare strings (a plain sentence, not JSON) get a synthetic
__inputNkey so they don't break the document.
And a nice ergonomic touch: connect input_1 and the next input slot auto-spawns, up to input_24. So you can keep stacking fragments without hunting for a "plus" button. Empty unused slots are silently ignored.
Inputs and outputs
Inputs: title (default character_1), output_format (same trio as the rest of JB - pretty_json, compact_json, loose_keys), and the 24 string inputs. Outputs: raw_json (always strict) and string (chosen format). In the reference workflow, string goes to CLIPTextEncode positive and raw_json goes to a sidecar/save node for debugging or reuse.
Installing
cd ComfyUI/custom_nodes
git clone https://github.com/ping1979ping/comfyui-FVMtools
No extra dependencies; the whole JB suite ships together under FVM Tools/JB. Restart ComfyUI after cloning.
The trap
The "last wins" rule is positional, so input ordering is semantics - feed a fragment in the wrong order and a field silently changes value with no error. If you're building a big character tree, get in the habit of checking the raw_json output (feed it into a ShowText or Save Text node) once before you commit to a batch. And the title behavior is the thing people reach for second: if you set title to an empty string, you lose the wrapping block entirely, which changes what the downstream encoder sees. The title is the point - it's what anchors an attribute to a character in a multi-character document.
Inputs (26)
| Name | Type | Default | Description |
|---|---|---|---|
| title | STRING | character_1 | — |
| output_format | COMBO | loose_keys | 4 options: pretty_json, compact_json, loose_keys, natural |
| input_1opt | STRING | — | |
| input_2opt | STRING | — | |
| input_3opt | STRING | — | |
| input_4opt | STRING | — | |
| input_5opt | STRING | — | |
| input_6opt | STRING | — | |
| input_7opt | STRING | — | |
| input_8opt | STRING | — | |
| input_9opt | STRING | — | |
| input_10opt | STRING | — | |
| input_11opt | STRING | — | |
| input_12opt | STRING | — | |
| input_13opt | STRING | — | |
| input_14opt | STRING | — | |
| input_15opt | STRING | — | |
| input_16opt | STRING | — | |
| input_17opt | STRING | — | |
| input_18opt | STRING | — | |
| input_19opt | STRING | — | |
| input_20opt | STRING | — | |
| input_21opt | STRING | — | |
| input_22opt | STRING | — | |
| input_23opt | STRING | — | |
| input_24opt | STRING | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| raw_json | STRING | — |
| string | STRING | — |