Nodes/ComfyUI-AudioReactive/Modulo (Audio Op)
ComfyUI Node

Modulo (Audio Op)

Modulo — thin out a beat train by taking every kth peak (and why it's a stub)

By dmarx·Created 3 years ago·Updated 2 years ago· 11
Modulo (Audio Op)
  • 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 - Rms or Novelty (or Predominant_Pulse) - through SignalToCurve and 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/offset inputs 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.

CategoryAudioReactive/Operators

Inputs (1)

NameTypeDefaultDescription
signalSIGNAL

Outputs (1)

NameTypeDescription
SIGNALSIGNAL