ποΈ Universal Timer
Universal Timer Puts a Stopwatch On the Canvas, Not in the Menu Bar
What this thing actually is
Universal Timer is a big, dumb, extremely readable stopwatch that sits on your graph as a node. Red seven-segment digits, an all-caps status line, a color picker. You drop it into a workflow, hit Queue, and watch 00:00:00 start climbing while your sampler grinds through a 60-step run at 1024Γ1024.
That's the pitch, and it's a real niche, because the timing you already have lives in the wrong place. Crystools puts a progress bar and elapsed time up in the menu bar; rgthree adds one across the top of the window; comfyui-profiler writes per-node timings as labels on the nodes. All fine. But when you're recording a screen or staring at 40 nodes wondering whether the sampler or the upscale killed your coffee break, a clock in the graph is the one you can point at. That was the original ask for this class of tool - progress without panning.
It doesn't break time down per node, though - one elapsed clock for the run, plus a progress counter. Need "which node is slow"? You want a profiler.
How it works, mechanically
The Python side is about 90 lines. UniversalTimer registers as an output node with zero outputs - RETURN_TYPES = (), OUTPUT_NODE = True. So it's a pure terminal: nothing wires out of it, and it exists only so the executor will fire it. When it runs, it stamps a start time in a module-level manager object, increments a completed counter, and broadcasts a websocket event (universal_timer_update) carrying the current count, the total, and which unit label to draw.
The clock itself is frontend work. A JS extension patches app.queuePrompt, so hitting Queue posts to a small /universal_timer/reset route and starts a 100ms repaint loop - which is why the digits tick smoothly between server events instead of jerking forward once per node. All the LED drawing (segments, colon, swatches) is canvas painting in onDrawBackground.
IS_CHANGED returns float("NaN"), the usual trick for forcing a node to re-run every time. On most nodes that's a trap - it poisons the cache behind them. Here it's free, since the timer has no upstream.
The two widgets, and the outputs
Two fields, and only one of them does anything.
total_count (INT, 1β999999, default 1) is the functional one - the denominator of the [ 3 / 12 ] readout. The author's rule, from the README: native tensor batching (batch_size = 4 in a KSampler) is one execution cycle, so leave it at 1 and read the elapsed time as a single unit. Sequential loops - a loop node, a batch image loader, anything that re-triggers the graph N times - set total_count to N, and the counter walks up as the timer node re-executes each round.
workflow_type (enum: Image, Video, Audio, Prompt, Training, Custom) is a label. Genuinely, literally a label - it maps to IMAGES, FRAMES, CLIPS, STEPS, EPOCHS, ITEMS and that's the whole effect. Image pipelines output images even if you spent three hours doing ControlNet passes in the middle; that's the author's classification rule. Don't go looking for hidden behavior in it.
Outputs: none. Not a hidden one - none at all. Anything queued through the API gets an empty result, so treat it as UI-only.
Installing it
Manager is the sane path: Custom Nodes Manager β search ComfyUI-Universal-Timer β Install β restart. Or manually:
cd ComfyUI/custom_nodes
git clone https://github.com/hermankwan/ComfyUI-Universal-Timer
Then restart. Find it by right-clicking the canvas β utils β Universal Timer, or search the name.
No models, no build step, no requirements.txt. pyproject.toml lists aiohttp, but the code only imports that and server.PromptServer, both shipped with ComfyUI - so nothing extra installs.
Where people get burned
This is v0.1.0 from a single author, first commit October 2026, with essentially no community track record yet. Judge it accordingly: it won't break your workflow (no inputs, no outputs, no dependencies), but don't expect polish.
The pause is cosmetic. Hover the counter and click [ PAUSE ] and you freeze the display, not the job - generation keeps going and the progress count keeps updating.
Refresh the page mid-run and the clock restarts. Elapsed time is frontend accounting, reset on load; the current / total counter re-syncs on the next tick. Clicking the digits zeroes it too.
Requeueing mid-run doesn't reset it. The reset endpoint ignores requests while a run is in flight, so queueing three prompts in a row gives you one continuous clock across all three rather than three clean timings.
It's a canvas-painted node, so Nodes 2.0 is a risk. Every pixel is custom drawing code - exactly the class of node ComfyUI's Vue-based frontend rewrite has broken across other packs. If the digits render wrong or the hover zones stop responding, switch back to the legacy canvas.
And the boring one: it has to be in the executed graph. Bypass it or mute its group and you get no timing - the node is the trigger.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| workflow_type | COMBO | Image | 6 options: Image, Video, Audio, Prompt, Training, Custom |
| total_count | INT | 11β999999 | β |
Outputs (0)
No outputs