Nodes/ꘇ Genereti/ꘇ chop.lfo
ComfyUI Node

ꘇ chop.lfo

The oscillator that can count in bars instead of seconds

By languel·Created about a month ago·Updated 3 days ago· 1
ꘇ chop.lfo
    • channels
    • value
    ◄wave▾►
    ◄period2.00►
    ◄low0.00►
    ◄high1.00►
    ◄phase0.00►
    ◄seed0►
    ◄intervalfree►
    ◄speed1.00►
    ◄smooth0.00►
    ◄time0.00►
    ◄samples1►
    ◄sample_rate60.00►
    ◄performance{}►

    ꘇ chop.lfo is a low-frequency oscillator: a value that sweeps between two numbers on a cycle, forever, in the background. It's the node you wire into a parameter you want to move without clicking - opacity, displacement strength, filter cutoff, a rope of 14 other numbers all breathing together.

    What it is

    Genereti's chop.* family is a control-signal layer for ComfyUI, named after TouchDesigner's channels: a CHOP is a named block of float samples with a sample rate, and any numeric widget on any node can be converted to an input and driven by one. chop.lfo is the oscillating source in that layer, alongside chop.constant, chop.expression, chop.lag and chop.gesture.

    Why you'd want a modulation source in a graph that mostly renders stills: the pack's whole premise is that a workflow can be a running performance, not a batch job. An LFO is how a parameter becomes alive. It's also the cheapest way to break up a repetitive batch - vary a strength per frame instead of stamping out identical output.

    How it works

    The LFO computes a phase, maps a waveform onto it, normalises that to your low..high range, then optionally smooths it. The waveform list is short and familiar: sine, triangle, saw, reverse saw, square, sample & hold (steps to a new random value each cycle, holding it), and smooth noise (interpolates between random values). seed makes sample & hold and smooth noise reproducible - same seed, same sequence.

    The interesting part is interval. Set it to free and period means seconds, like any LFO you've used. Set it to a musical interval - 4n, 8n, 8nt, 4nd, 2m, 1m and friends, where t is triplet and d dotted - and the LFO follows the workflow's project transport instead: its phase is derived from quarter notes and the project BPM. 2m means a two-bar cycle. That means your visual modulation can breathe in bars while the audio sequencer stays on the beat, which is the single most useful thing about this node.

    phase offsets the cycle by up to ±16 turns, speed (0.01–16) multiplies the rate, and smooth (0–10) applies an exponential filter to the output - a few seconds of smooth turns a square wave into a slow, organic swell rather than a hard switch. Both the live browser path and the queued path evaluate the same maths, with the queue using explicit time and samples rather than a live clock.

    The inputs and outputs that matter

    Inputs: wave, period (0.01–600 s, default 2), low (default 0) and high (default 1) as the output range, phase, seed, interval (default free), speed, smooth, time, samples and sample_rate (default 60), plus an optional performance JSON string if something upstream is feeding you clock state.

    Outputs: channels (a CHOP block with one channel named lfo) and value (a FLOAT - the latest value).

    For 95% of use cases you want value into a converted numeric input, and you're done. The channels block is for when you want the LFO to arrive as a signal - feeding an instrument, or passing through chop.math to reshape it before it drives anything. Remember samples: with the default of 1 you get one sample per delivery and smooth live control; raise it for a block.

    Install

    Manager → search Genereti - or, from your ComfyUI directory:

    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, refresh the browser tab. One dependency line (comfyui-frontend-package>=1.53.6), nothing to download, no models anywhere in the CHOP layer. Comfy ≥ 0.38.2, Python ≥ 3.11. Note that interval values like 2m only make sense with the performance timeline in play - open Workflows → Genereti → ꘇ-Performance-Timeline and toggle the dock with Alt+Shift+T.

    Common issues

    The LFO runs, but nothing downstream moves. The usual cause is that you typed a value into a widget instead of converting it to an input. Right-click the target widget → Convert widget to input → wire value in. A wired input overrides the widget.

    The range is wrong - everything is 0 or 1. Check low and high; they're your output range, not the waveform's. If the parameter you're driving expects 0–100, set it there rather than adding a chop.math multiplier.

    Musical intervals don't feel musical. They follow project transport. If chop.lfo is set to 4n and there's no project clock running, or you're running composited with a free-time setting, the phase reference isn't what you expect. free is the safe mode for standalone patches.

    sample & hold looks frozen. With a long period it holds one value for the whole cycle - that's the waveform, not a bug. Shorten period or raise speed.

    The value updates in bursts. Control updates are limited by browser frames, and samples above 1 spreads deliveries out further. Drop to samples = 1 for responsive scalar sweeps.

    Standard caveat: this is a solo MIT alpha (Liuboto, v0.1.4), self-described as "super alpha - early classroom testing", so keep backups of workflows you care about.

    CategoryGenereti / CHOP

    Inputs (13)

    NameTypeDefaultDescription
    waveCOMBO7 options: sine, triangle, saw, reverse saw, square, sample & hold, +1
    periodFLOAT2.000.01–600—
    lowFLOAT0.00-100000–100000—
    highFLOAT1.00-100000–100000—
    phaseFLOAT0.00-16–16—
    seedINT00–100000—
    intervalCOMBOfree19 options: free, 4m, 2m, 1m, 1n, 2nd, +13
    speedFLOAT1.000.01–16—
    smoothFLOAT0.000–10—
    timeFLOAT0.00-100000–100000—
    samplesINT11–4096—
    sample_rateFLOAT60.001–1000—
    performanceoptSTRING{}—

    Outputs (2)

    NameTypeDescription
    channelsGENERETI_CHOP—
    valueFLOAT—