Nodes/ComfyUI Batch Image Timer/⏱️ Batch Image Timer
ComfyUI Node

⏱️ Batch Image Timer

A Retro LED Stopwatch for Your Folder Runs

By hermankwan·Created 25 days ago·Updated 2 days ago· 2
⏱️ Batch Image Timer
      ◄total_images1►

      Queue up a folder of 200 photos and hit Run, and ComfyUI gives you almost nothing to look at. The preview flickers, the terminal scrolls, and after twenty minutes you still can't say whether you're 30% done or 3%. This node fixes exactly that one thing - and it does it with a 7-segment LED clock drawn on the canvas, because of course it does.

      What it is and why you'd reach for it

      If you've ever batch-processed a directory in ComfyUI, you know the ritual: point a sequential loader at the folder, then set the queue count next to the Run button to the exact number of files so the loader advances its index once per execution. It's the standard trick, and it's annoying - change the file count and you have to remember to change the number too.

      Batch Image Timer slots into that workflow as instrumentation. Drop it anywhere in the graph, and while the queue grinds through your images it shows elapsed time as HH:MM:SS on LED digits, a live current / total counter, and a state: READY before you queue, CANCEL if you hit Interrupt, ERROR if something upstream throws. The digits are clickable to reset, and you can hover the counter to PAUSE if you wander off and don't want your break counted in the average.

      Be honest about what it is: a plumbing node, not a capability. On a single-image workflow it's decoration. On an overnight 400-image upscale, it's the difference between "it's fine" and "it's been stuck for an hour".

      How it works

      It's an output node with zero outputs. Nothing wires out of it, and nothing has to wire into it - it just has to be in the graph so the engine executes it, once per prompt cycle. That's the whole mechanism on the backend:

      RETURN_TYPES = ()
      FUNCTION = "process"
      OUTPUT_NODE = True
      
      @classmethod
      def IS_CHANGED(cls, **kwargs):
          return float("nan")
      

      That float("nan") is the classic always-rerun trick: the engine compares IS_CHANGED results and NaN is never equal to anything, so the node fires every queue even if nothing upstream changed. Without it, ComfyUI's cache would skip the timer on the second run and you'd stare at a frozen clock. It also defeats the cache downstream - fine here, because there's nothing downstream of it.

      Each time process() runs, the node increments a counter and broadcasts a websocket event through PromptServer.send_sync with current, total, and elapsed seconds. web/batch_image_timer.js catches that, draws the LED digits with onDrawBackground, and - since v0.1.1 - ticks the clock client-side with performance.now instead of trusting server timestamps (that release fixed "multi-batch time jumping / rebound bugs"). It subscribes to ComfyUI's executing, status, execution_error, and execution_interrupted events to know when to stop and which state to show. Your LED color lives in the node's own properties, so it saves inside the workflow JSON rather than in a global setting - copy the workflow and the color travels with it.

      The one input that matters

      There's exactly one: total_images (INT, default 1, max 999999). It is not a count of pixels or a batch size - it's how many times you expect the graph to execute.

      • Native tensor batching (batch_size = N on a latent or sampler): leave it at 1. ComfyUI runs that as one monolithic prompt execution, so the timer correctly reports one unit of work.
      • Sequential / iterative loops (sequential image loaders, image-to-image folder runs): set it to N, or wire it in from your loader's count if that loader exposes one, and the node keeps current / total honest and derives your average per-image time.

      Get this backwards and the numbers lie: the frontend only calls the run finished once current >= total, so total_images = 500 on a run of 50 sits at 50/500 forever, and 1 on a 50-image loop stops the clock at the first image.

      Installation

      No model files, no heavyweight dependencies. Two ways:

      cd ComfyUI/custom_nodes
      git clone https://github.com/hermankwan/ComfyUI-Batch-Image-Timer
      

      then restart ComfyUI. Or use ComfyUI Manager → Custom Nodes Manager → search ComfyUI-Batch-Image-Timer → Install.

      The README claims no external Python dependencies, and the repo backs that up: no requirements.txt, and the only entry in pyproject.toml is aiohttp - which ComfyUI already runs PromptServer on. Nothing genuinely new gets installed. Find the node under right-click → utils / timer, or search Batch Image Timer in the node search.

      Common issues

      The LED digits don't show up at all. The node paints its UI straight onto the LiteGraph canvas and hooks app.queuePrompt - exactly the class of third-party rendering ComfyUI's Nodes 2.0 frontend rewrite keeps breaking (the same story as rgthree's stacked text fields). If it renders as an empty box, switch back to the legacy canvas.

      Pause doesn't pause anything. Correct - it isn't supposed to. Pausing freezes the UI ticking only; the backend keeps sampling. It's for excluding your own away-time from the elapsed total, nothing more.

      Two timer nodes disagree / count each other's work. The backend keeps its counter and start time on class-level attributes - one timer per server process, not per node. Two of these in one graph share the same clock and counter. Use one.

      The clock keeps running after the queue emptied. The stop condition is the count reaching total_images, so an early interrupt, a loader that ran fewer files than expected, or a wrong total_images value leaves it ticking. Click the LED digits to reset to 00:00:00.

      State stuck on ERROR or CANCEL. Those are sticky until the next queue - that's the design, not a bug.

      Categoryutils/timer

      Inputs (1)

      NameTypeDefaultDescription
      total_imagesINT11–999999—

      Outputs (0)

      No outputs