MetaHub Timer
A stopwatch node that makes your 'generation time' metric honest
- clip
- image
- latent
- conditioning
- clip
- image
- latent
- conditioning
- elapsed_time
Every generation-time number in your saved metadata is only as honest as how it was measured. The MetaHub Save nodes can auto-track timing, but "start to finish" is fuzzy when your graph includes upscalers, ControlNet preprocessors, or a second sampling pass. MetaHub Timer is a tiny, single-purpose node that solves that: it records a timestamp the moment it executes, and the save node turns that into a real elapsed-time value for the analytics chunk. It's a stopwatch you drop into the graph at exactly the point you care about.
The mechanism is deceptively simple. The node has four optional pass-through inputs - clip, image, latent, and conditioning - and it returns the same four unchanged, plus a FLOAT called elapsed_time. When ComfyUI executes it, it stamps time.time(). Wire that elapsed_time into the save node's generation_time_override and the save node computes the elapsed duration from the timestamp. That's the whole trick.
The critical detail hiding in the source: the node returns a NaN from IS_CHANGED, which forces ComfyUI to re-execute it on every run instead of caching its output. Without that, a cached run would hand you a stale timestamp and your timing would be meaningless. It's the kind of subtle correctness fix that separates a working timer from a decorative one.
Where to place it
The inputs are all pass-through, so you inline the timer on the branch you want to measure - connect the input, then connect the matching output onward to keep your graph intact.
- Timer at your CLIP loader, with
clippassed through to CLIPTextEncode → total workflow time from load to save. - Timer after the KSampler → sampling plus decode time.
- Timer after VAEDecode → just the save and post-processing leg.
You can add several, each feeding its own save node or profiling different stages. The README suggests exactly this for benchmarking: one timer early for the full run, one after the sampler for the heavy lifting, one after decode for the tail.
When you'd actually use it
Honestly, for most people this is a "nice to have, until it isn't" node. If you never look at the performance section of your metadata, you can skip it entirely and the save nodes still work. But the moment you start benchmarking workflows - comparing samplers, checking whether a new upscale path actually costs you anything - the difference between "auto-timed" and "stopwatch-accurate" shows up fast. It's also the piece that makes the video node's analytics meaningful, since video runs get even fuzzier on auto-timing.
Install and gotchas
Part of the same pack as the rest: ComfyUI Manager, search ImageMetaHub Save, or:
cd ComfyUI/custom_nodes
git clone https://github.com/LuqP2/ImageMetaHub-ComfyUI-Save.git
Restart and you're done - no dependencies, no models. The one trap is placement: the timer only fires if it actually executes, so it has to be on the execution path, which means you must pass one of its inputs through rather than leaving it dangling. And remember the output you care about is elapsed_time, not the passthroughs - the save node does the timestamp math, so connect it to generation_time_override on your MetaHub save node. That's the whole setup; it's one of those rare custom nodes that does exactly one thing and does it without getting in the way.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| clipopt | CLIP | — | |
| imageopt | IMAGE | — | |
| latentopt | LATENT | — | |
| conditioningopt | CONDITIONING | — |
Outputs (5)
| Name | Type | Description |
|---|---|---|
| clip | CLIP | — |
| image | IMAGE | — |
| latent | LATENT | — |
| conditioning | CONDITIONING | — |
| elapsed_time | FLOAT | — |