A collection of ComfyUI utility custom nodes. These provide functionality not offered in the core app or other custom nodes. Several nodes enhance existing ideas with improved UX and quality-of-life features.
- YFG Comical ComfyUI Custom Nodes
- Nodes
- Image Histograms Generator
- Image Histograms Generator (compact)
- Image Halftone Generator
- Image Side by Side
- Image to imgBB
- Smart Checkpoint Loader
- Mono Clip
- VAE Decode with Preview
- Image to Contrast Mask
- PixelArt
- Text Mask Overlay
- Image Switchers
- Random.org True Random Number
- Random.org True Random Number (V2)
- Random Image From Directory
- Random Prompt From File
- Display Value
- CivitAI MetaSave
- Examples
- All nodes as of 06-13-2024
- Acknowledgements
- Nodes
Table of contents generated with markdown-toc
Calculate image histograms for RGB channels and L (luminance); displays a graphical representation.
Reduces outputs to just two: Original and Histogram. Can self-preview (original or histogram) and forward the selected histogram downstream.
Based on original code by Phil Gyford and ComfyUI node by aimingfail.
Generates halftone from an input image; can self-display or send output to other nodes.
Creates a side-by-side or split image from two inputs. Optional self-preview, headers (font/size/color), and label toggles.
Example (side-by-side instead of split):
Upload and download images to/from imgBB. Includes nodes for downloading images and a URL "storage" node that keeps the original link alongside the workflow.
Create imgbb_api_key.json under ./loaders/:
{
"api_key": "YOUR_API_KEY_HERE"
}Obtain a key at https://api.imgbb.com/.
Drop-in replacement for the core "Load Checkpoint," but flattens complex directory trees so checkpoints appear as if in a single folder — ideal for sharing workflows.
Produces B/W or grayscale clipped images with three modes (each reversible). Useful for masks and stylization.
Based on original code by XSS.
VAE decode with inline preview and QoL options.
Based on original code by XSS.
Creates a grayscale contrast mask. Select low/high thresholds (1–255), optional blur.
Based on original code by XSS.
Generates pixel-art style images. Choose interpolation and pixel size.
Based on original Mosaic code by XSS.
Enhances the idea by Yuigahama Yui:
- Choose fonts from System, User, or local
./fonts/directory. - Built-in Mask-to-Image conversion (no extra converter needed).
Example
![]() |
![]() |
|---|---|
| Original | Output |
Example workflow JSON (also embedded in the image below):
Based on the ComfyUI-Text node by Yuigahama Yui.
Multi-input image switchers (3, 5, 10, 15, 20 inputs). Provide a routing matrix, inline preview, and graceful handling of missing inputs (with user warnings). Designed to avoid "ganged" switcher confusion in complex workflows.
Modified version of the WAS node (original wasn't functioning). Requires a Random.org account and API key.
Based on original code by WASasquatch.
Integrates with the random.org JSON-RPC API to generate true random numbers from atmospheric noise.
-
Secure API key handling
Key is not exposed in UI, workflow JSON, or image metadata. Loaded at runtime from:- Environment variable
RANDOM_ORG_API_KEY, or - Local JSON file
random_org_api_key.jsonnext toRandomOrgV2.py.
- Environment variable
-
Flexible outputs
Emits three types for broad node compatibility:NUMBERFLOATINT
-
Optional uniqueness filtering (in-memory)
Avoid repeats within a Python session:ensure_unique— toggle de-duplicationunique_scope—"range"(per[min, max]) or"global"(across all ranges)history_size— how many values to remembertime_window_sec— ignore duplicates older than this windowretry_limit— extra draws to try before giving up
-
Backwards compatible
Old node remains available asRandomOrgTrueRandomNumber_node.
New node isRandomOrgV2TrueRandomNumber_node.
-
Create
random_org_api_key.jsonnext toRandomOrgV2.py:{ "api_key": "YOUR_API_KEY_HERE" } -
Or set an environment variable before launching ComfyUI:
export RANDOM_ORG_API_KEY=YOUR_API_KEY_HEREOn Windows (PowerShell):
setx RANDOM_ORG_API_KEY "YOUR_API_KEY_HERE"
-
Generate a random integer between 1 and 100
minimum = 1maximum = 100mode = random
-
Ensure no repeats in the current range
- Enable
ensure_unique - Set
unique_scope = range - Adjust
history_sizeandtime_window_sec
- Enable
- Session-lifetime uniqueness resets when Python restarts.
- Persistence (disk-based history) may be added in a future version.
- API limits: Random.org quotas apply — check your dashboard.
This node selects a single image from a given directory (with optional recursion into subdirectories).
It supports true randomness (via Random.org integration if configured) or deterministic selection by index/filename.
It also tracks previously selected images so you can compare "current" vs "previous" outputs.
- Built-in directory browser
- 📁 Browse for Directory button opens a navigable server-side folder picker — no need to type paths manually.
- 🕐 Recent Directories button shows an MRU list of previously used directories for quick re-selection.
- Used directories are saved automatically to
yfg_dir_history.jsonon each run.
- Inline output value display
- Directory traversal
- Select only from the top folder, or include all subdirectories.
- Multiple selection modes
random: choose a random image (true random if Random.org is enabled, otherwise local PRNG).by_index: pick image by numeric index (clamped to valid range — no wrap-around).by_filename: select a file by exact name or substring.by_query: glob-style matching (*.png,cat*, etc.), random among matches.
- Uniqueness filtering
- Avoids repeating the same file within the same session.
- Scope can be per-directory or global.
- Adjustable
history_sizeandtime_window_secfor fine control.
- Metadata outputs
- Current/previous file info, image size, and SHA256 hash of the file.
- Also reports
total_count= number of eligible images found.
image_directory(string) – Path to the folder containing images.include_subdirs(bool, default: True) – Whether to scan subdirectories.selection_mode(choice, default: random) – Image selection method.index(int) – Used whenselection_mode=by_index.filename_query(string) – Used whenselection_mode=by_filenameorby_query.random_source(choice, default: auto) –auto: use Random.org if API key is configured, else local PRNG.local: always use local PRNG.random_org: force Random.org usage (requires API key).
ensure_unique(bool) – Prevent repeats during a session.unique_scope(choice) –"directory"or"global".history_size(int, default: 512) – Max remembered items.time_window_sec(int, default: 0) – Forget items older than this many seconds.show_preview(bool, default: False) – Show a preview thumbnail on the node after each run.retry_limit(int, default: 16) – Max retries when avoiding duplicates.
image– The loaded image tensor.path_current– Full path of the selected image (current).index_current– Index of the selected image (current).filename_current– Filename of the selected image.width– Width of the image in pixels.height– Height of the image in pixels.sha256– SHA256 checksum of the file (useful for deduplication).total_count– Total number of eligible images discovered in the directory (after filters).path_previous– Full path of the image from the previous run.index_previous– Index of the image from the previous run.
- To enable true randomness, place your API key in
random_org_api_key.jsonnext to the node:
-
Create
random_org_api_key.jsonnext toRandomOrgV2.py:{ "api_key": "YOUR_API_KEY_HERE" } -
Or set an environment variable before launching ComfyUI:
export RANDOM_ORG_API_KEY=YOUR_API_KEY_HEREOn Windows (PowerShell):
setx RANDOM_ORG_API_KEY "YOUR_API_KEY_HERE"
- Current vs previous outputs make it easy to compare selections.
- Session-lifetime uniqueness resets when Python restarts.
- Directory history is persisted to
yfg_dir_history.jsonand survives restarts (max 50 entries). - API limits: Random.org quotas apply — check your dashboard.
Selects a single prompt entry from a .txt prompt file using true random.org numbers (or local PRNG). Outputs positive, negative, and name text directly — no additional nodes or wiring required.
- Built-in file browser
- 📄 Browse for File button opens a navigable server-side file picker showing directories and
.txtfiles, with a live prompt count badge per file. - 🕐 Recent Files button shows an MRU list of previously used files for quick re-selection.
- Used files are saved automatically to
yfg_file_history.jsonon each run.
- 📄 Browse for File button opens a navigable server-side file picker showing directories and
- Auto range population
range_startandrange_endfill automatically (0 and total−1) whenever a file is selected — no manual entry needed.
- Last-N toggle
last_n_onlyrestricts the random pool to the last N entries in the file — ideal when new prompts are appended to the bottom and you want to pick only from the newest additions.
- Inline output value display
index_current,index_previous, andtotal_countare shown directly on the output slots after each run.
- INDEX auto-sync
- The INDEX widget updates automatically after every run so switching to
by_indexmode requires no manual typing.
- The INDEX widget updates automatically after every run so switching to
- True random.org integration
- Uses the same Random.org API key as the Random Number nodes — no separate setup needed.
- Uniqueness filtering
- Avoids repeating prompts within a session with configurable history size and time window.
- Shuffle bag
- Cycles through all prompts in the pool before repeating, then reshuffles — guarantees uniform coverage.
positive: your positive prompt here
negative: ugly, deformed, watermark
----
positive: second prompt
negative:
name: Optional Label
----
- Entries separated by any number of hyphens (
-,--,----) on their own line negative:content may be emptyname:is optional — defaults to the filename stem- Leading/trailing separator lines are silently ignored
- UTF-8 encoding required
prompt_file(string) – Path to the.txtprompt file. Use 📄 Browse or 🕐 Recent buttons.selection_mode(choice, default: random) –randompicks fromrange_start..range_end;by_indexuses the INDEX widget directly.index(int) – Used whenselection_mode=by_index. Auto-syncs after every run.range_start(int, default: 0) – First prompt index in the random pool. Auto-fills to 0 when a file is selected.range_end(int, default: 0) – Last prompt index in the random pool. Auto-fills to total−1 when a file is selected. 0 = last prompt.last_n_only(bool, default: False) – When ON, restricts the pool to the last N entries only.last_n_count(int, default: 100) – How many entries from the end to include whenlast_n_onlyis ON.random_source(choice, default: auto) –auto,local, orrandom_org.ensure_unique(bool, default: True) – Avoid repeating prompts within a session.history_size(int, default: 100) – How many recent indices to track for uniqueness.time_window_sec(int, default: 0) – Forget entries older than this many seconds.retry_limit(int, default: 20) – Max attempts before falling back to last candidate.use_shuffle_bag(bool, default: True) – Cycle through all prompts before repeating, then reshuffle.
positive– Positive prompt text.negative– Negative prompt text (empty string if not specified in file).name– Prompt name from thename:field, or filename stem if omitted.index_current– 0-based index of the selected prompt. Auto-syncs to INDEX widget.index_previous– Index of the prompt selected in the previous run this session.total_count– Total number of valid prompts found in the file.
Uses the same random_org_api_key.json file as the Random Number nodes — see Random.org True Random Number (V2) for setup instructions.
- Session-lifetime uniqueness resets when Python restarts.
- File history is persisted to
yfg_file_history.jsonand survives restarts (max 20 entries). - File cache — parsed prompt files are cached in memory keyed by modification time. Re-parsing only occurs when the file changes on disk — zero overhead on repeat runs.
- API limits: Random.org quotas apply — check your dashboard.
A minimal utility node that displays any value — INT, FLOAT, or STRING — inline in both the node body and the title bar. Designed to replace large display nodes (like ShowText or Display Int) wherever a compact read-only indicator is all that's needed.
- Title bar display — value shown as
value | labelso it's always visible even when the node is collapsed to its smallest size. - In-body display — green value rendered in the node body when expanded, no extra height added.
- Rename to label — double-click the node title in ComfyUI to set a custom label (e.g.
Index,Width,Seed). The value is appended automatically after each run. - Any type — accepts
INT,FLOAT, orSTRINGon the single input slot via wildcard. - Persistent — last value is saved into the workflow JSON and pre-filled on reload.
value(any) – The value to display.
- No outputs — this is a pure display/monitoring node.
- The node title format is
value | label. Rename the node to change the label; the value portion updates automatically on each run.
Saves images with full A1111-compatible metadata embedded so that CivitAI automatically recognizes and displays generation info — model, LoRA weights, embeddings, sampler settings, seed, and prompts — exactly as it does for Automatic1111 outputs.
A single node replaces the two-node workflow required by other metadata extensions. Four optional custom key/value extra-metadata pairs are built directly into the node — no helper nodes required.
Compatible with classic SD (KSampler, KSamplerAdvanced), Flux, SD3, Ideogram, SamplerCustomAdvanced, LoRA, and upscale workflows.
- CivitAI auto-recognition — embeds the
parameterstext chunk that CivitAI parses for resources, prompts, and settings, identical to A1111 output. - Multi-format output — PNG, JPEG, or WebP, each with an optional sidecar workflow JSON.
- EXIF embedding — JPEG and WebP outputs also receive EXIF UserComment metadata so CivitAI can read them regardless of format.
- Model & resource hashing — SHA-256 hashes (first 10 hex chars) for checkpoint, VAE, LoRA, and embedding files are computed and cached on disk to avoid redundant re-hashing.
- LoRA detection — finds LoRA weights from both LoraLoader nodes and inline
<lora:name:weight>syntax in the prompt text. - Filename tokens — supports
%date:yyyy-MM-dd%,%seed%,%model%,%pprompt:32%,%nprompt:32%,%width%,%height%in the filename prefix. - Subdirectory support — optional sub-folder creation inside the output directory, also supporting date tokens.
- Metadata scope control — choose what gets embedded: full, default, parameters only, workflow only, or none.
- Four inline extra metadata slots — attach custom key/value pairs (e.g. workflow name, prompt index, project tag) without any additional nodes.
images(IMAGE) – Images to save.filename_prefix(string, default:ComfyUI) – Output filename prefix. Supports tokens:%date:yyyy-MM-dd%,%seed%,%model%,%pprompt:32%,%nprompt:32%,%width%,%height%.subdirectory_name(string) – Optional sub-folder inside the output directory. Supports%date%tokens. Leave blank to use the default output folder.output_format(choice, default:png_with_json) – Image format. The*_with_jsonvariants also write a sidecar.jsonworkflow file.png/png_with_jsonjpg/jpg_with_jsonwebp/webp_with_json
quality(choice, default:max) – Output quality level. Ignored for PNG.max— 100 (lossless WebP)high— 80medium— 60low— 30
metadata_scope(choice, default:full) – Controls what metadata is embedded.full— A1111 parameters string + full ComfyUI workflow JSONdefault— same as the built-in SaveImage nodeparameters_only— A1111-style parameters string onlyworkflow_only— ComfyUI workflow JSON onlynone— no metadata
include_batch_num(bool, default:false) – Append a 5-digit batch index to the filename when saving multiple images.prefer_nearest(bool, default:true) – When multiple upstream nodes provide the same field, prefer the one closest (fewest graph hops) to this node.extra_key1/extra_value1(string, optional) – Custom metadata key/value pair 1.extra_key2/extra_value2(string, optional) – Custom metadata key/value pair 2.extra_key3/extra_value3(string, optional) – Custom metadata key/value pair 3.extra_key4/extra_value4(string, optional) – Custom metadata key/value pair 4.
piexifmust be installed (pip install piexif). It is listed as a dependency inpyproject.tomland should install automatically via ComfyUI Manager.- Hash cache is stored in
civitai_metasave/.cache/model_hash_cache.json. Hashes are computed once per file and reused on subsequent runs — no performance penalty after the first generation. - Steps are required for full metadata. If no Steps value is found upstream, the parameters string is skipped and a warning is printed to the console. This is a CivitAI requirement, not a bug.
- Workflow attribution — derived from comfyui_image_metadata_extension by edelvarden which itself was a fork of ComfyUI-SaveImageWithMetaData. Maintained and extended independently as part of YFG Comical.
If the embedded workflow doesn't load, use the JSON:
workflows/ComfyUI_YFG_Comical-Example-Workflow.json
Huge thanks to creators whose work inspires or integrates with these nodes:
…and many others. Thank you for your talent and generosity.


























