ꘇ top.temporal
The Shimmer Killer That Does Nothing When You Hit Run
- image
- image
Here's the thing to know before anything else: ꘇ top.temporal is a Live-mode node. Hit Queue and it returns your input unchanged. Not "roughly the same" - the queued implementation is literally a pass-through, because temporal state lives in the browser GPU, not in Comfy's graph. If you ran it, saw no difference, and closed the tab, that's why.
What it actually does is temporal cleanup: a normalized previous/current blend that stabilizes a flickering source - aliased edges, dithered gradients, sensor noise, the shimmer on thin lines - without the motion smear you'd get from a blunt frame average.
How it works
The node holds two GPU textures and ping-pongs between them. For each new frame it blends in history with a weight that falls off as pixels change:
weight = strength * clamp(1 - change / change_threshold, 0, 1)
out = mix(current, clamp(previous, 3x3 neighborhood), weight)
Three guards keep that from turning into ghost soup:
- Per-pixel change rejection. Change is measured on premultiplied RGBA, so a moving edge loses its history almost immediately instead of trailing a dirty halo. Lower
change_thresholdrejects more motion. - Neighborhood clamping. History is clamped to the min/max of the current 3×3 neighborhood before blending, which is what stops stale values from bleeding into areas the image has left behind.
- Scene-cut rejection. A 4×4 probe samples sixteen positions across the frame; if the average delta says the whole picture changed, history is dropped for that frame rather than smeared across the cut.
History advances only on new source dispatches. The pack compares dispatch serials, not GPU texture handles, precisely because producers reuse handles - a held frame or a repeated timestamp won't stack up blending. Rewiring, dimensions changing, settings changing, seeking, source loss or pressing the ↺ button all reset history. The reason to care: a filter that keeps blending while nothing new arrives quietly turns into a long-exposure smear behind your back.
It's also independent of ꘇ top.feedback. That node is the artistic one - decay, transform, trails. This is cleanup. Keeping the two apart means you can stabilize a source and still keep the deliberate feedback smear on top of it.
The two controls
- strength - 0 to 0.95, default 0, which is off. Start around 0.3. Higher values hold more history and can lag subtle motion.
- change_threshold - 0.001 to 1, default 0.12. This is the rejection threshold: lower values reduce ghosting, because more pixels count as "changed" and stay current.
They pull against each other, so tune in that order - strength up until the flicker dies, then threshold down until the moving parts stop smearing. That's the whole tuning loop, and it takes about thirty seconds live because you're watching the frames.
One output: image, so it slots in wherever an IMAGE goes. Best placement is early - immediately after your source, before ꘇ top.bloom, ꘇ top.feedback or a displacement chain. Cleaning up after those means you're stabilizing a decision you've already amplified.
Installing it
The usual single-folder install:
cd ComfyUI/custom_nodes
git clone https://github.com/languel/genereti.git genereti
./.venv/bin/python -m pip install -r genereti/requirements.txt # ComfyUI's Python
Windows: .\.venv\Scripts\python.exe. Portable: python_embeded\python.exe. Don't use global pip. There are no model downloads and no heavy dependencies - the requirement is effectively a frontend floor, and torch/NumPy/Pillow come from Comfy itself. Manager search "Genereti" works while the registry listing is up; git always does. Restart ComfyUI, then hard-refresh the browser.
Since this node is Live-only in practice, budget for a modern WebGPU-capable browser. That's a real constraint on older laptops and remote/headless setups - if Live can't get a WebGPU adapter, you get an error instead of a fallback render.
Where people get burned
- Queue does nothing. Covered above, but it's the number one report-shaped confusion: Queue returns the current image, no history, no error. If your output needs to be temporal in a queued render, this node is not that node.
- Strength 0 looks broken. Zero disables history by design - the default is "installed but inert," which is a good default and a bad first impression.
- It lags the motion. It's a heuristic, not motion-compensated reconstruction. Fast pans and subtle moving edges will still smear at high strength; that's physics of what it's doing, not a bug to tune away.
- The image jumps when you touch a setting. Changing dimensions or settings resets history - deliberate, so stale state can't leak into a new configuration. Expect the pop.
- Frame rate changes the look. Blends happen per delivered frame, so the same strength reads differently at 24 fps versus 60.
- Live and Queue differ in precision. Live intermediates are 8-bit rgba8unorm; Queue keeps floats. You mostly won't notice, but don't expect bit-identical results across the two paths.
And the pack-level warning, which matters more here than anywhere: Genereti self-describes as a super-alpha classroom release. Back up workflows before updating. top.temporal holds only transient GPU state, so nothing is serialized to lose - but the rest of your graph is.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| strength | FLOAT | 0.000–0.95 | History blend; zero disables history. Start around 0.3; higher values can lag motion. |
| change_threshold | FLOAT | 0.120.001–1 | Reject changing pixels; lower values reduce ghosting. Broad scene cuts also reject history. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |