Nodes/ꘇ Genereti/ꘇ chop.slope
ComfyUI Node

ꘇ chop.slope

How fast the value is changing, per second

By languel·Created about a month ago·Updated 3 days ago· 1
ꘇ chop.slope
  • input
  • channels
  • value

ꘇ chop.slope differentiates. Every sample becomes the difference from the previous one, multiplied by the sample rate - so the output is in units per second, which is the version of a derivative that's actually comparable between a 60 Hz block and a 240 Hz one. Feed it a knob, get a velocity. Feed it a gate, get an edge.

What it is

Most signal chains are about level; slope is about change. That's what makes it the odd, useful node in the family: it's the one that answers "is this moving, and how fast?" rather than "what is it?" Wire an LFO through it and you get a value that peaks as the wave crosses zero and sits at zero at the extremes - the opposite of the wave itself, which is exactly the kind of inversion that makes a control feel alive rather than mechanical.

It's also the node that makes edge detection possible without a trigger node. A step from 0 to 1 through ꘇ chop.logic becomes a one-sample spike of +sample_rate at that transition, because the difference of 1 times the rate is the rate. Multiply by 1/sample_rate if you want a clean 1.0 edge, or threshold it again. The pack doesn't ship a dedicated rising-edge node, so this is the idiom.

How it works

Per channel, per sample: the previous sample is subtracted and the result is scaled by the block's sample rate. The very first sample of a block has no predecessor, so the node prepends the current value to the diff - meaning sample zero always comes out as 0 and you get an honest flat start rather than a spurious spike from an uninitialized buffer.

The important behavioural detail is state. Live, Slope carries its last value across blocks: the browser keeps the previous sample per channel in the node's memory and reuses it for the next block, so a chain of 1-sample blocks differentiates continuously instead of resetting 60 times a second. The reset button (↺) clears that memory along with the preview history, which is what you want after seeking the timeline or rewiring. Queued, there is no memory - the node processes one explicit block from scratch, so the first sample is always 0.

Non-finite results are scrubbed to zero before they leave the node, so an upstream division blow-up shows up as a flat line rather than NaN.

The inputs and outputs that matter

  • input - the GENERETI_CHOP to differentiate. Channel names pass through unchanged; ꘇ chop.select upstream if you only care about one.
  • channels out - the rate-of-change block, same names, same sample rate, same block length.
  • value out - FLOAT, last sample of the first channel: the current rate of change. Wire it to a converted numeric widget when you want "speed of movement" as a parameter.

There are no parameters. No gain, no smoothing, no window - a single-sample difference is noisy by nature on a jittery input, and the fix is to smooth the input with ꘇ chop.lag first rather than to look for a setting. Sample rate matters twice over: it sets the units on the output and it's the value you'd divide by to undo the scaling.

Because it's stateful, this is also the node that behaves most differently between Live and Queue. If a parameter follows your hand in the browser and then does nothing on a queued run, that's not a bug - that's a derivative with no history.

Install

Every operator in these pages ships in one pack. Manager → search genereti (title "ꘇ Genereti") → install → restart ComfyUI → hard-refresh the browser tab. Manual:

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

That pip step needs your ComfyUI's Python. The requirements file is one line - comfyui-frontend-package>=1.53.6 - so there are no models, no extra server, and nothing that can break another pack's dependency. ꘇ-OpenTouch-Operators-and-Lessons under Workflows → Genereti is the place to see the processors in a working chain.

Common issues

Flat zero on Queue. Expected: no history between queued blocks. If you need a derivative in a batch context, generate the samples inside one block (larger samples upstream) rather than across blocks.

A 0 at the start of every block. Also expected, and by design.

Jittery output. A single-sample diff amplifies noise, and at 60 blocks a second with one sample each it amplifies a lot. Smooth first: ꘇ chop.lag upstream is the standard fix, and the same trick applies to ꘇ chop.speed.

Nothing resets when you seek. The node clears its memory on a project seek, but if you've been editing the chain by hand the stale value can linger. Press ↺ - the reset button exists for Slope, Speed and Lag specifically.

Cycles are rejected. Feeding a derivative back into its own upstream path raises "CHOP cycles are not supported" in the node's status line.

Super alpha. The README's own framing: early classroom testing, expect bugs and changing interfaces, back up workflows before updating.

CategoryGenereti / CHOP

Inputs (1)

NameTypeDefaultDescription
inputGENERETI_CHOP—

Outputs (2)

NameTypeDescription
channelsGENERETI_CHOP—
valueFLOAT—