Universal Project Config
The master control board that replaces spaghetti wiring
- initial_upscale
- final_upscale
- tight_crop
- blend_opacity
- stitch_opacity
- fuzz_tolerance
- edge_uniformity
- crop_pad_px
- subject_mask
- inpaint_prompt
- outpaint_prompt
- stitch_mask
- config_bundle
- seed
Every big ComfyUI workflow ends the same way: the same handful of numbers duplicated across a dozen nodes - upscale factors, crop toggles, mask prompts, seeds - and every tweak means a hunt through the graph. UniversalProjectConfig is the pack's attempt to end that. It's a single settings panel with one output per setting, plus a serialized bundle you can unpack anywhere with ExtractConfigValue. The README calls it "the ultimate master control board," and for once the marketing isn't lying.
It's schema-driven, which is the interesting part under the hood. The node reads a JSON schema (config_schema.json) that lists every setting - initial_upscale, final_upscale, tight_crop, blend_opacity, stitch_opacity, fuzz_tolerance, edge_uniformity, crop_pad_px, subject_mask, inpaint_prompt, outpaint_prompt, stitch_mask, plus a seed - and generates the UI widgets and the output pins dynamically from it. Add a setting to the schema, and the node grows a new input and output. That means the node you see is the full inventory of settings the pack's photo pipeline cares about, in one place.
The outputs are the thing. It emits every setting as its own typed pin - floats for upscale factors and opacities, booleans for tight_crop, strings for the prompts - so you can wire each value directly to the node that consumes it without running wires across the whole graph. And it also emits config_bundle, a JSON string containing everything. That's the "wireless" path: put one ExtractConfigValue next to any consumer, feed it the bundle, pick a key, done. No cross-graph wire, no duplicated constant.
The seed handling is worth knowing because it's non-obvious: a seed of -1, -2, or -3 makes the node produce a fresh random seed on each execution (that's what the IS_CHANGED override is doing), and it resolves against the graph's prompt/extra_pnginfo for per-run stability. So you can drive random-seed workflows from the same panel that drives everything else.
A full photo-restoration graph built around this pack reads like a real program: config in one corner, ExtractConfigValue nodes sprinkled where values are consumed, and the prompt fields (subject_mask, inpaint_prompt, outpaint_prompt) populated once instead of per-node.
Install
Manager → search "PortraitUtils", or:
cd ComfyUI/custom_nodes
git clone https://github.com/heyburns/PortraitUtils
Restart. No extra dependencies - it's a schema-driven JSON/config node.
Common issues
- A setting you set doesn't seem to reach the node - you changed the input but the consuming node reads the bundle path (
ExtractConfigValue) which only refreshes when the bundle string changes. Re-execute the graph after editing. ExtractConfigValuereturns a default instead of your value - the bundle key wasn't there (schema mismatch on an old graph) or the extract node is reading a stale bundle. Re-run and check the key name.- Seeds repeat across runs - if you set
seed: -1expecting randomness but it's stable, remember-1/-2/-3map to random, and 0 is a real seed. If it's wired throughExtractConfigValue... don't do that; the config outputs the seed directly. - The node looks "empty" or missing pins - schema didn't load. If the pack updated, restart ComfyUI so the schema re-reads.
It's the node that makes a multi-stage photo workflow editable from one window. If you've ever refactored a graph just to change one number, this is the refactor.
Inputs (13)
| Name | Type | Default | Description |
|---|---|---|---|
| initial_upscale | FLOAT | 2.00.1–100 | — |
| final_upscale | FLOAT | 4.00.1–100 | — |
| tight_crop | BOOLEAN | false | — |
| blend_opacity | FLOAT | 0.500–1 | — |
| stitch_opacity | FLOAT | 1.000–1 | — |
| fuzz_tolerance | FLOAT | 0.070–0.5 | — |
| edge_uniformity | FLOAT | 0.850–1 | — |
| crop_pad_px | INT | 00–64 | — |
| subject_mask | STRING | — | |
| inpaint_prompt | STRING | — | |
| outpaint_prompt | STRING | — | |
| stitch_mask | STRING | — | |
| seed | INT | 00–4294967295 | — |
Outputs (14)
| Name | Type | Description |
|---|---|---|
| initial_upscale | FLOAT | — |
| final_upscale | FLOAT | — |
| tight_crop | BOOLEAN | — |
| blend_opacity | FLOAT | — |
| stitch_opacity | FLOAT | — |
| fuzz_tolerance | FLOAT | — |
| edge_uniformity | FLOAT | — |
| crop_pad_px | INT | — |
| subject_mask | STRING | — |
| inpaint_prompt | STRING | — |
| outpaint_prompt | STRING | — |
| stitch_mask | STRING | — |
| config_bundle | STRING | — |
| seed | INT | — |