comfyui-jz
Jacques' personal ComfyUI nodes (Gemini outpaint, padding, extras)
Nodes (19)
Stitch the Original Back Without the Seam
Kill the One-Pixel Fill-Color Rim Gemini Leaves Behind
Gemini image generation billed in real dollars
Make Your Canvas Match What Gemini Actually Returns
The before/after wipe, baked into frames instead of eyeballed in a viewer
The pick that survives a reordered list
The paste node that ends the size math
Stop squinting at raw strings
A trimap from two sliders
The long edge answer in two INTs
The branch that only runs when it has to
The bouncer your batch workflows are missing
A captioner that fails on purpose
The Resize And Pad your outpainting actually wants
Taming mixed-size batches
The Resolution Selector You Can Actually Wire Into a Graph
Resize without stretching the subject
One line out of many, on a seed you control
Lazy if/else that mutes dead branches
comfyui-jz
personal comfyui nodes, in jz/ category so they never mixes with the installed packs + they are easy to find.
when editing existing nodes, ONLY append widgets... so that old workflows still work
contents
- jz/api : jz Gemini Generate, jz OpenRouter VLM, ...
- jz/image : jz Composite Back, jz Seam Repair, jz Seam Carve, ...
- jz/util : jz String Picker, jz Fallback, jz Switch, jz Display JSON, ...
nodes
jz/api
custom nodes using https calls, with retries on 429/5xx response, the (api) keys are resolved server-side (either in an .env or a config.ini) and should never be stored in workflows!
- jz Gemini Generate, vertex or generativelanguage generateContent. it works with zero images (text-to-image), single images, batches or a proper image list. when using
batch_size, it fires parallel calls (shared token, with per-call retries). the outputs are the image plus a usage summary and total token count. api key is the base64 encoded service account, stored in the.envasSERVICE_ACCOUNT_BASE64=....aspect_ratioandresolutionare dropdowns, built from the same dimension table as jz Pad Calculator so they can't drift (minusauto, which is a pad-calculator fitting mode, not an api value). to drive them from a wire instead, use theaspect_ratio_in/resolution_insockets — connected beats the dropdown, and the value is checked against the api's list before a request is spent on it

- jz OpenRouter VLM, vision/text through openrouter. it raises on errors instead of passing them downstream. also downscales images before upload (
max_edge), and sends every frame of a batch as a separate image in one call (so "describe the two images" just works), and outputs the cost. thereasoningwidget defaults tolow(reasoning models otherwise burnmax_tokenson hidden thinking and return truncated answers). api key is stored in theconfig.ini, under the [API] router asOPENROUTER_API_KEY=...

jz/image
plain image ops (often image in image out), no API involved
- jz Composite Back, pastes the original back onto the generated image with feathered edges [outpainting workflow]

- jz Seam Repair, deterministically cleans leftover fill-color seams at the canvas edge [outpainting workflow]

- jz Pad Calculator, picks the best supported aspect/resolution (for Nano Banana Pro) for an image and computes the padding to get there [outpainting workflow]

- jz Resize Long Edge (list), normalizes a list or batch of mixed-size images to one long edge, outputs a list (of images).
interpolationpicks the resample method (same five as jz Resize And Pad); a frame already at the target size is passed through untouched, and alpha is preserved

- jz Seam Carve, content-aware resize (Avidan-Shamir seam carving + forward energy, arXiv:2608.04329). carve or enlarge either dimension, protect/remove regions with MASK inputs. numba-compiled fast path when numba is installed, multi-frame batches carve in parallel. note: forward energy algorithm will cut through flat uniform regions, protect the product with a mask when it matters!

- jz Edge Sizes, outputs the long and short edge of an image as INTs (width/height sorted, orientation-agnostic)

- jz Composite, pastes a source image onto a destination at a named anchor (center, corners, edge midpoints) with x/y offset and a border margin. optional MASK blends the source through it (a 4-channel source blends through its own alpha). no scaling — resize upstream. raises if the source doesn't fit

- jz Double Threshold, two-sided binarization: luma above
highgoes white, belowlowgoes black, the band in between goes transparent. outputs the RGBA image (feeds jz Composite's alpha blending directly), a trimap MASK (1/0.5/0) and the decided-pixels alpha MASK

-
jz Image Sanity, flags degenerate frames — empty (0 px, always checked), flat, too dark, too bright, fully transparent — each check toggleable with its own threshold. it never raises on a bad frame: it measures and reports, so you branch on
okwith jz Switch / jz Fallback and decide yourself whether that means retry, substitute or skip. flatness is measured per channel (a flat red frame has channel stds of 0 but a whole-tensor std of ~0.47, so a single global std would call it textured). takes a batch or an image list, and the per-frame outputs (image,ok,reason,std,mean) are lists — one verdict per frame;report(json, feeds jz Display JSON) andall_okare scalars for whole-batch decisions -
jz Resize And Pad, comfyui's own Resize And Pad Image with the padding colour set free — it only offers white or black. same fit-and-centre (
min(tw/w, th/h), always scale to fit), butpadding_colortakes#rrggbb/#rgb/black/white, or wire a solid-colour image intocolor_image(jz Pad Calculator'sfill_color) and it's sampled from there. also outputs the padding region as a MASK (1 = bar, 0 = image), and channels are preserved — an RGBA input stays RGBA with the padding opaque. note:interpolationdefaults tolanczos, which round-trips through 8-bit in comfy's implementation; pickareaorbicubicto stay in float. a frame that already matches the target is passed through untouched rather than resampled -
jz Resolution Selector, aspect ratio → width/height. comfyui's core Resolution Selector labels its options
16:9 (Widescreen)and takes a COMBO, which no STRING output can connect to — so it can't be driven from jz Pad Calculator. this one uses plain ratios, andaspect_ratio_inis a STRING socket you can actually wire (connected beats the dropdown; it also accepts the core node's parenthesised form).modepicks how the size is computed:tablereturns the exact dimensions gemini emits (straight from the same DIMENSION_MAP as jz Pad Calculator, 10 ratios — core has 8, this adds4:5and5:4),megapixelsuses core's own formula for any ratio and any target. they differ slightly on purpose: 16:9 at 1 MP is1368x768by the formula but1376x768in the table -
jz Before/After Slider, animates a wipe between two images: a divider sweeps across revealing
afteroverbefore, holds, sweeps back — so the batch loops seamlessly. outputs the frames as an IMAGE batch (plusframe_countandfps), so you pick the encoder:VHS_VideoCombinefor a gif, or core'sSaveAnimatedWEBP/SaveAnimatedPNG/SaveWEBM. frames stay editable, so you can composite a caption on them first. timing is two numbers —sweep_secondsandhold_seconds(the start-end hold is split across the loop seam, so a looping player dwells equally at both ends).scaleresizes both inputs first: it's the lever on output size, and the handle scales with it (1.0skips resampling entirely).orientationswitches to a horizontal divider. mismatched input sizes raise — match them with jz Resize And Pad
jz/util
- jz String Picker, picks one string from a list (one per line or custom separator), random (seeded) or by wrapping index

- jz Fallback (lazy if/else), passes
primaryif present, otherwise evaluatesfallback. the unused branch never executes

- jz Switch (lazy if/else), boolean-driven: outputs
on_trueoron_falsedepending oncondition, the other branch never executes. both branches are optional: if the selected one is not connected, downstream nodes are silently skipped (if true output the image, else output nothing)

- jz Display JSON, takes a string of json and renders it in the node as a collapsible syntax-highlighted tree (copy button included). invalid json shows a parse-error banner + the raw text instead of killing the run. the view survives save/reload, and the prettified string passes through as output

- jz Choice, picks from a list of choices (STRING input, one per line or another separator) by NAME — reordering the list upstream never silently changes the pick, it either still matches or raises listing the options. when the choices come from a string-literal node, the
choicewidget turns into a real dropdown (live-refreshed); with runtime-computed choices it stays a text field. outputs the value and its index
