ꘇ chop.speed
The integrator, and a free stopwatch
- input
- channels
- value
ꘇ chop.speed accumulates. Each output sample is the running total of everything that came before it, divided by the sample rate - so a constant input of 1 becomes a ramp climbing one unit per second. It's the integral to ꘇ chop.slope's derivative, and the pair of them means you can move between "how fast" and "how far" anywhere in a chain.
What it is
Most of the CHOP processors are stateless filters: value in, value out. Speed is one of the three that remember something (the others being ꘇ chop.slope and ꘇ chop.lag), which changes how you think about the node. It's not transforming a signal, it's accumulating one. The plainest use is the stopwatch: feed it a constant 1 and you have a counter of elapsed seconds that runs while the graph runs, no expressions involved.
The second use is position from velocity. If ꘇ chop.slope on a slider gives you how fast you're turning a knob, speed on that gives you total travel - which is a surprisingly good driver for anything you want to feel "wound up" rather than "set", a filter amount that keeps climbing while you keep moving, a texture rotation that accumulates drift instead of oscillating.
How it works
Per sample: multiply by nothing, divide by nothing, add value / sample_rate to the running total. That's the whole mechanism, and the units fall out of it - sum of (input per sample) divided by samples per second gives input-units per second of accumulation. Feed a 0..1 signal and after ten seconds at 1.0 you're at 10.0. Feed a −1..1 oscillating signal and you get a wandering walk rather than a rise.
State is where the subtlety lives. Live, the node keeps its accumulator per channel between blocks, so a chain of 1-sample blocks behind an oscillator integrates smoothly. Queued, it does not: the whole block is summed from zero, so the first sample of a queued run is whatever the first input sample is worth, and there's no history from previous runs. Re-seek the project timeline and the accumulator resets with it; the ↺ button clears it manually.
There is no wrap, no clamp and no reset input. A gate that sticks on, or a constant with a positive value, makes the output climb forever - and since the only network here is the FLOAT socket downstream, an unbounded value can look like a broken parameter rather than a runaway integral.
The inputs and outputs that matter
- input - the
GENERETI_CHOPto integrate. Channel names pass through untouched. - channels out - the accumulating block, one accumulator per channel, same sample rate and start time as the input.
- value out - FLOAT, the last sample of the first channel: your running total. This is the socket you'd wire into a converted numeric input, and because it's monotonic it's a good fit for "amount of work done" style controls and a terrible fit for anything that needs to stay in range.
No parameters at all - no gain, no rate multiplier, no reset port. If you need a bounded version, put ꘇ chop.math with clamp after it (low/high), or fit to remap into a range. That's the intended composition: generators and processors stay single-purpose, and the range wrangling happens downstream.
Install
All these operators ship 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
Use the Python that belongs to your ComfyUI. The requirement is comfyui-frontend-package>=1.53.6 and nothing else, so there's no model download and no chance of a dependency fight with your other packs. Workflows → Genereti → ꘇ-OpenTouch-Operators-and-Lessons shows the processors in a real chain, and ꘇ-Performance-Timeline is where time and seeking are explained.
Common issues
It resets when you queue. By design - a queued block has no history. If you want accumulation inside a batch run, generate more samples per block upstream and integrate within one block.
It climbs off the map. With any input that isn't roughly zero-mean, the accumulator only goes one way. Clamp downstream, or put ꘇ chop.logic in front so you're integrating a 0/1 curve (which gives you time spent on, in seconds - a genuinely useful measurement).
Stale total after rewiring. Speed keeps its accumulator across graph edits, so a chain that looked sane can carry an old offset. The ↺ reset button clears memory for Speed, Slope and Lag; a project seek does the same.
Nothing moves on Queue but your parameter did earlier. Live delivery is capped at roughly one update per browser frame, so a 1-sample block at 60 samples a second accumulates one step per frame. Cranking sample_rate to 1000 changes the units on the output, not the number of updates you get.
Alpha status. The README calls the pack super alpha with early classroom testing: back up workflows before updating, expect interfaces to move under you.
Inputs (1)
| Name | Type | Default | Description |
|---|---|---|---|
| input | GENERETI_CHOP | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| channels | GENERETI_CHOP | — |
| value | FLOAT | — |