ꘇ chop.math
The CHOP you'll wire into half your numeric sockets
- input
- channels
- value
ꘇ chop.math takes a signal and does arithmetic on it: multiply, add, subtract, divide, absolute value, clamp, or fit. Seven operations, one node. It's the least glamorous node in the pack and probably the one you'll place most often, because it's the standard adapter between a signal that swings the way its generator wants and a control that only accepts 0..1.
What it is
Genereti treats signals the way ComfyUI's plumbing layer treats primitives: one value, many consumers. The difference is that a CHOP's value is a block of samples that keeps updating in the browser while the graph is idle, not a number you typed. That makes chop.math the equivalent of "convert widget to input" for animated controls - it's how a TouchDesigner-style operator chain (a signal, a scaler, a target parameter) gets expressed in a node graph whose sockets are plain FLOATs.
The author's own audio demo is exactly this pattern: ꘇ mod.analyze → ꘇ chop.select (rms) → ꘇ chop.math at ×12 → the amount input of ꘇ top.filter. A 0..1 RMS level becomes a 0..12 filter drive, with no code and no per-frame Python in the loop.
How it works
Per channel, per sample. Multiply/add/subtract/divide apply value to every sample and pass channel names through unchanged. abs gives you abs(sample) and ignores value. clamp limits each sample between low and high. fit remaps an input assumed to be in −1..1 onto low..high by low + (v + 1) * 0.5 * (high - low).
Two bits of behaviour worth internalizing. First, divide guards against zero: if value is within 1e-9 of zero it divides by 1e-9 instead of blowing up, so a slider parked at 0 gives you a huge number rather than NaN. Second, non-finite results are scrubbed to 0 before the block leaves the node (NaN, +inf, −inf all become 0), which will hide a broken upstream value as a flat line instead of an error.
Everything runs on Float32Array blocks in the browser. There is no persistent state in this node - no accumulation, no memory between blocks - which is why it's safe to leave in a chain and unsafe to assume anything about after a pause.
The inputs and outputs that matter
- input - the
GENERETI_CHOPto process. - operation - one of
multiply,add,subtract,divide,abs,clamp,fit. The COMBO holds all seven whatever you're doing, so it's easy to pickclampand then wonder why the ×2 you set invalueis doing nothing. - value - default
1, range ±100000. Used by multiply, add, subtract and divide; ignored byabs,clampandfit. It's a scalar: every channel gets the same treatment. - low / high - default
0and1, only read byclampandfit. - channels out - the processed block, same channel names, same sample rate.
- value out - FLOAT, the last sample of the first channel. This is the one you connect to a numeric widget. If your block has several channels and you want a specific one, put
ꘇ chop.selectupstream rather than guessing which one comes first.
The fit operation is the one people misread. It isn't "normalize to the observed min and max" - it's a fixed linear map from a −1..1 source. Feed it a 0..1 signal and you don't get your full range back; you get the bottom half, then clamp-free overshoot at the top. For unipolar sources, multiply then add, or clamp, is what you want.
Install
One pack, one install for every node below. ComfyUI Manager → search genereti (title "ꘇ Genereti") → install → restart ComfyUI → hard-refresh the browser tab. Or by hand:
cd ComfyUI
git clone https://github.com/languel/genereti.git custom_nodes/genereti
./.venv/bin/python -m pip install -r custom_nodes/genereti/requirements.txt
Install that requirements file with your ComfyUI's Python (.venv/bin/python, or python_embeded\python.exe for Windows Portable). It contains comfyui-frontend-package>=1.53.6 and nothing else - no models, no torch rebuild, no extra server. Reference environment is ComfyUI 0.38.2 with frontend 1.53.6 on Python 3.11+.
Common issues
Queue gives you one frozen block. These nodes are live in the browser; a queued run evaluates explicit samples from the node's time and sample_rate values. If you queue a graph whose only value source is an oscillator, don't be surprised when the output is deterministic and boring.
Live updates cap out around 60 a second. Channel computation is cheap, but delivery is tied to animation frames, and the node's samples/sample_rate control the signal timeline, not the redraw rate. One sample per block is the right setting for a responsive scalar control.
The status line echoes errors. If the node shows something like "Connect CHOP input", the upstream block never arrived - usually a ꘇ chop.switch with nothing connected on the selected source, or a cycle, which the live graph rejects with "CHOP cycles are not supported".
Alpha software. The README calls it super alpha with early classroom testing: back up workflows before updating, expect interfaces to move.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| input | GENERETI_CHOP | — | |
| operation | COMBO | 7 options: multiply, add, subtract, divide, abs, clamp, +1 | |
| value | FLOAT | 1.00-100000–100000 | — |
| low | FLOAT | 0.00-100000–100000 | — |
| high | FLOAT | 1.00-100000–100000 | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| channels | GENERETI_CHOP | — |
| value | FLOAT | — |