Nodes/ꘇ Genereti/ꘇ chop.lag
ComfyUI Node

ꘇ chop.lag

The one-knob fix for twitchy control signals

By languel·Created about a month ago·Updated 3 days ago· 1
ꘇ chop.lag
  • input
  • channels
  • value
◄seconds0.10►

ꘇ chop.lag slows a signal down. You give it a CHOP and a number of seconds, and it eases toward the incoming values instead of jumping to them. Two sockets, one widget, and it fixes the single most common problem with any live control signal: it's too twitchy to look good.

What it is

Genereti's chop.* nodes are a control-signal layer for ComfyUI - a TouchDesigner-style vocabulary of named float channels that can drive any numeric parameter. These signals come from chop.lfo, chop.expression, chop.constant, chop.gesture, and from the analysis taps on the audio side (mod.analyze and friends, which publish rms, peak and band channels).

Almost all of those are jumpy. Raw RMS off a drum track spikes on every hit; a recorded mouse gesture has hand tremor in it; a step-sequenced value snaps from one number to the next. chop.lag is the smoothing stage you put between the source and the parameter, and it's the difference between a visual that pulses pleasantly and one that strobes.

How it works

Mechanically it's a one-pole exponential filter. For each sample, the output moves a fixed fraction of the way toward the input:

k = 1 - exp(-1 / (sample_rate * seconds))
out[i] = out[i-1] + (in[i] - out[i-1]) * k

That formula is why the single control is seconds rather than "amount". seconds is the time constant: it's the rough scale of how long the output takes to catch up with a change. Small values track almost instantly; large values trail lazily behind. The curve is smooth and asymptotic - the output approaches the target without ever overshooting, which is exactly what you want when the target is driving visible motion. No ringing, no wobble, no bounce.

Two details from the implementation worth knowing. The filter is stateful within a block: it seeds itself from the first sample of the incoming block and then runs through the rest, so a block of 60 samples at 60 Hz is smoothed sample-by-sample, not block-by-block. And it's applied once, at the node - chain two lags and the time constants compound, which is a legitimate trick if you want a two-stage ease but a confusing accident if you didn't.

Because this operates on the CHOP samples rather than on a live clock, queue and live behaviour agree: the node evaluates explicit samples and time when you queue, rather than reading wall-clock state.

The inputs and outputs that matter

  • input - the CHOP to smooth. GENERETI_CHOP, required.
  • seconds - 0.00001 to 100, default 0.1. The time constant. 0.1 s is a light touch that removes jitter; 1–2 s makes a slow, syrupy follow; 10 s is essentially "drift".

Outputs: channels (the smoothed CHOP block, channel names preserved) and value (a FLOAT - the last sample of the first channel). For most graphs the value output is the useful one: convert a numeric widget to an input, wire value in, and your parameter now moves smoothly.

Where it belongs in a chain: anywhere the incoming signal is a measurement or a performance gesture. mod.analyze's rms → chop.lag → a brightness parameter is the canonical example, and it's a two-node fix for the "why does my visual flicker on every snare" question.

Install

Manager, search Genereti, or:

git clone https://github.com/languel/genereti.git custom_nodes/genereti
./.venv/bin/python -m pip install -r custom_nodes/genereti/requirements.txt

Restart ComfyUI and reload the browser page. The CHOP family pulls in nothing extra - requirements.txt is one line (comfyui-frontend-package>=1.53.6), and NumPy, torch and Pillow are already in ComfyUI. No models, no downloads. Comfy ≥ 0.38.2, Python ≥ 3.11.

Common issues

The output doesn't respond at all. Check the source: if the upstream node isn't producing live values - a queued analysis tap returns zeros, for instance - lag faithfully smooths zero. Start the audio output and watch it live, or check that the LFO feeding it is actually oscillating.

Everything is over-smoothed. seconds is a time constant, not a percentage, so 5 means five seconds of catch-up and looks broken on a 2 Hz LFO. Halve it, twice.

Live control feels sluggish no matter what. Look at samples and sample_rate upstream: delivery rate is limited by browser frames, and with samples above 1 you get fewer, larger blocks, so the arrival granularity is coarse even if the maths is fine. Set samples = 1 for smooth scalar control.

And the usual: this pack is a solo MIT alpha by Liuboto (v0.1.4) whose README says "super alpha - early classroom testing. Back up your workflows before updating."

CategoryGenereti / CHOP

Inputs (2)

NameTypeDefaultDescription
inputGENERETI_CHOP—
secondsFLOAT0.100.00001–100—

Outputs (2)

NameTypeDescription
channelsGENERETI_CHOP—
valueFLOAT—