Modulo (Audio Op)
Modulo — thin out a beat train by taking every kth peak (and why it's a stub)
- signal
- SIGNAL
OpModulo (display name "Modulo (Audio Op)") is supposed to do something kind of clever and kind of janky: detect the peaks in a signal, then keep only every k-th one, so a busy hit train becomes a sparse, rhythmic skeleton. The source shows the intent - it runs SciPy's find_peaks, enumerates the peaks, and keeps only those where (index + offset) % k == 0, building a new signal that's zero everywhere except the chosen peak samples.
Why would you want that? Think of the classic audio-reactive problem: the track hits every 16th note, but you want your animation to react on the quarter notes - half-time energy, breathing room between pulses. "Keep every kth beat" is a dead-simple way to get from "too busy" to "grooves harder," and that's the entire job of this node. The author even left a TODO in the source about supporting an offset so you could grab even vs. odd peaks.
Here's the honest part: the node doesn't work yet. It's one of the pack's parameterized operators whose wrapper was never finished.
What's actually going on
modulo is a factory - modulo(k=2, offset=0) returns a function. The node wrapper in nodes/audio_operator_nodes.py calls every operator as f(y, sr), so it calls modulo(y, sr) with the signal as k and the sample rate as offset. That doesn't thin peaks; it returns a closure parked in the SIGNAL's y slot. The node completes without an error, and downstream anything that does real math on the signal throws a TypeError. The info_schema confirms the half-finished state: the node only exposes a signal input - no k, no offset. A user literally can't set the thinning factor.
What to do instead
- If a workflow contains "Modulo (Audio Op)", treat it as broken and route around it.
- Want every-kth-beat behavior today? Take a beat feature -
RmsorNovelty(orPredominant_Pulse) - throughSignalToCurveand do the thinning in the curve domain: most keyframe/scheduler tooling lets you gate or sub-sample, which is arguably cleaner than peak-picking anyway. - Watch the repo. The backend is real (find_peaks + modulo is a complete idea); it just needs the wrapper to expose
k/offsetinputs and call the factory with them.
Inputs and outputs
As shipped:
signal(SIGNAL) - in.SIGNAL- out, but containing a function object, not a thinned signal. Don't build on it.
Installing it
Manager (search "AudioReactive") or:
cd ComfyUI/custom_nodes
git clone https://github.com/dmarx/ComfyUI-AudioReactive
Restart; first load auto-installs scipy (which provides find_peaks), scikit-learn, librosa, loguru. If the pack won't load with ModuleNotFoundError: No module named 'keyframed', pip install keyframed.
The takeaway
Modulo is a great idea wearing a broken wrapper - a symptom of a pack that's a fresh, single-commit port of dmarx's video-killed-the-radio-star notebook. The working operators here (Rms, Novelty, Stretch, Smoosh, Normalize, Pow2, Harmonic, Percussive, Predominant_Pulse) are worth building on; the parameterized ones (Modulo, Bandpass, Clamp, Pow, Quantize, Smooth, Sustain) are placeholders. Skim nodes/audio_utils.py before trusting any of them.
Inputs (1)
| Name | Type | Default | Description |
|---|---|---|---|
| signal | SIGNAL | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| SIGNAL | SIGNAL | — |