Restructure for future growth and packaging clarity: * hold_counter_node.py -> hold_counter/node.py (git rename, history preserved). Top-level __init__.py is now a thin ComfyUI entry point that re-exports NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS, and WEB_DIRECTORY from the new subpackage. * New hold_counter/__init__.py exposes HoldCounter, NodeState, MODES, DEFAULT_MODE, and the ComfyUI mappings as the package public API. * pyproject hatch wheel target simplified to packages = ["hold_counter"] (the source-mapping and force-include hacks are no longer needed); pyright include list updated to the new layout. * tests now import hold_counter.node instead of hold_counter_node. Fixes: * pyright reportUnknownVariableType on NodeState.perm — typed the dataclass default_factory as list[int]. * CHANGELOG MD024 (duplicate "### Added" / "### Fixed" headings under per-version sections) — disabled per-file via markdownlint comment; this is the conventional fix for Keep-a-Changelog files. Also folds in light user-applied formatting tweaks to web/hold_counter.js (comment wrapping) and workflows/hold_counter_basic.json (4-space indent / expanded form). No public behaviour change. The only "breaking" aspect is the import path for downstream Python consumers (hold_counter_node -> hold_counter.node); ComfyUI itself is unaffected since it loads custom_nodes/hold_counter/__init__.py.
ComfyUI-HoldCounter
A small but flexible utility node for ComfyUI that
emits a sequential integer index, holds it for a configurable number of consecutive queue runs,
and walks an inclusive [min_index, max_index] range using the advancement mode of your choice.
Useful as a Load Image batch index when you want each input image to be used for several consecutive
runs (e.g. multiple seeds per image) before advancing — and for many other batch automation
patterns.
Features
runs_per_index— how many consecutive queue runs hold each index (default1).min_index/max_index— inclusive output range (defaults0/1). Order doesn't matter; ifmin > maxthey're swapped automatically.mode— how the index advances:loop(default) — wrap frommaxback tomin.clamp— stop atmax, then keep emittingmaxforever.pingpong— bounce:0,1,2,3,4,3,2,1,0,1,….random— uniformly random in range each tick (still held forruns_per_indexruns).shuffle— walk a random permutation of the range; reshuffle after a full pass.
format— Python-style format string applied to the emittedindex_stroutput. Examples:"{}"→"7","{:04d}"→"0007","frame_{:03d}"→"frame_007". Bad templates fall back tostr(index)rather than raising.- Two outputs —
index(INT) andindex_str(STRING). Wire either or both. - Reset button in the node body — momentary; rewinds only this node's counter.
- Per-node state — multiple HoldCounter nodes in one workflow advance independently.
- Live display — the current
index_stris shown read-only inside the node body after each run.
Examples
Sequence of outputs for min_index=0, max_index=4:
| Mode | Output sequence (runs_per_index=1) |
|---|---|
loop |
0, 1, 2, 3, 4, 0, 1, 2, 3, 4, … |
clamp |
0, 1, 2, 3, 4, 4, 4, 4, … |
pingpong |
0, 1, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4, 3, … |
random |
each value uniformly random in [0, 4] |
shuffle |
random permutation per pass, e.g. 2, 0, 4, 1, 3, … |
Increasing runs_per_index simply repeats each emitted value that many times — for example,
mode=loop, runs_per_index=2 produces 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 0, 0, ….
Installation
Via ComfyUI Manager
- Open Manager → Install Custom Nodes.
- Search for
ComfyUI-HoldCounterand click Install. - Restart ComfyUI.
Manual install
cd ComfyUI/custom_nodes
git clone https://github.com/mitch-avis/ComfyUI-HoldCounter.git
Then restart ComfyUI. There are no Python dependencies beyond ComfyUI itself.
Usage
The node appears under utils → Hold Counter. Connect:
index(INT) → e.g. theindexinput of a Load Image (Batch) node.index_str(STRING) → e.g. a filename prefix on a Save Image node.
A ready-to-import example lives in
workflows/hold_counter_basic.json.
Notes
- Counter state is per-node (keyed by ComfyUI's internal
unique_id) and persists across queue runs within a single ComfyUI session. Restarting ComfyUI resets every counter. - The node is
OUTPUT_NODE = True, so it executes every queue submission even when its outputs aren't connected to anything downstream.
Development
cd ComfyUI/custom_nodes/hold_counter
uv venv && uv pip install -e ".[dev]"
uv tool run ruff format --check .
uv tool run ruff check .
uv run pyright .
uv run pytest --cov
CI runs the same checks across Python 3.10 / 3.11 / 3.12 on every push and PR.
License
MIT. See CHANGELOG.md for release notes.