Extensions/comfyui-jz
ComfyUI Extension

comfyui-jz

Jacques' personal ComfyUI nodes (Gemini outpaint, padding, extras)

By j-zhang19·Created about a month ago·Updated 6 days ago· 2
j-zhang19/comfyui-jz
Nodes19
On cloudLocal install
Categoryjz/image, jz/api
Stars2
Updated6 days ago
Readme

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 .env as SERVICE_ACCOUNT_BASE64=.... aspect_ratio and resolution are dropdowns, built from the same dimension table as jz Pad Calculator so they can't drift (minus auto, which is a pad-calculator fitting mode, not an api value). to drive them from a wire instead, use the aspect_ratio_in / resolution_in sockets — connected beats the dropdown, and the value is checked against the api's list before a request is spent on it

jz_gemini_generate

  • 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. the reasoning widget defaults to low (reasoning models otherwise burn max_tokens on hidden thinking and return truncated answers). api key is stored in the config.ini, under the [API] router as OPENROUTER_API_KEY=...

jz_openrouter_vlm

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_composite_back

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

jz_seam_repair

  • 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_pad_calculator

  • jz Resize Long Edge (list), normalizes a list or batch of mixed-size images to one long edge, outputs a list (of images). interpolation picks 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_resize_long_edge

  • 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_seam_carve

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

jz_edge_sizes

  • 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_composite

  • jz Double Threshold, two-sided binarization: luma above high goes white, below low goes 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_double_threshold

  • 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 ok with 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) and all_ok are 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), but padding_color takes #rrggbb / #rgb / black / white, or wire a solid-colour image into color_image (jz Pad Calculator's fill_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: interpolation defaults to lanczos, which round-trips through 8-bit in comfy's implementation; pick area or bicubic to 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, and aspect_ratio_in is a STRING socket you can actually wire (connected beats the dropdown; it also accepts the core node's parenthesised form). mode picks how the size is computed: table returns the exact dimensions gemini emits (straight from the same DIMENSION_MAP as jz Pad Calculator, 10 ratios — core has 8, this adds 4:5 and 5:4), megapixels uses core's own formula for any ratio and any target. they differ slightly on purpose: 16:9 at 1 MP is 1368x768 by the formula but 1376x768 in the table

  • jz Before/After Slider, animates a wipe between two images: a divider sweeps across revealing after over before, holds, sweeps back — so the batch loops seamlessly. outputs the frames as an IMAGE batch (plus frame_count and fps), so you pick the encoder: VHS_VideoCombine for a gif, or core's SaveAnimatedWEBP / SaveAnimatedPNG / SaveWEBM. frames stay editable, so you can composite a caption on them first. timing is two numbers — sweep_seconds and hold_seconds (the start-end hold is split across the loop seam, so a looping player dwells equally at both ends). scale resizes both inputs first: it's the lever on output size, and the handle scales with it (1.0 skips resampling entirely). orientation switches 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_string_picker

  • jz Fallback (lazy if/else), passes primary if present, otherwise evaluates fallback. the unused branch never executes

jz_fallback

  • jz Switch (lazy if/else), boolean-driven: outputs on_true or on_false depending on condition, 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_switch

  • 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_display_json

  • 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 choice widget turns into a real dropdown (live-refreshed); with runtime-computed choices it stays a text field. outputs the value and its index

jz_choice