ꘇ chop.noise
Chop.noise is white noise, not the drifting fields you're picturing
- channels
- value
If you came from TouchDesigner expecting a Noise CHOP with octaves, lacunarity and a smooth field, read the mechanism before you wire anything: ꘇ chop.noise produces uncorrelated sample-to-sample randomness. It's the signal equivalent of a dice roll per frame, which is great for jitter and dither and useless for organic drift. For coherent movement, use ꘇ chop.expression with its simplex/perlin/value functions - the author's performance docs say exactly that.
What it is
Generators in the CHOP family come in two flavours: things that produce a shape over time (ꘇ chop.oscillator, ꘇ chop.lfo), and things that produce an unpredictable value in a controlled range. This is the second kind, and it's the base of the class hierarchy - the oscillator and the expression node are literally subclasses of the same generator in the source. What it gives you is a named block of samples, plus a FLOAT for the last sample of the first channel, so it can drive any numeric widget a normal Comfy primitive would drive.
The KB's plumbing doc frames nodes like this as a way to stop retyping values into widgets. A noise generator is that idea with the value moving: you don't set the parameter, you let it wander.
How it works
Each sample is a deterministic hash: a sine of a large multiplier applied to sample index + floor(start * sample_rate) + channel * 131 + seed + offsets, scaled and rewritten to land in −1..1. Same inputs, same samples - always. That's a design decision with two consequences worth internalizing.
First, a queued run is reproducible: time is explicit in the queue, so you get the same block every time you run it. Your NODE_ID-free deterministic graph stays deterministic.
Second, it only animates if time advances, which in the live browser means the graph clock, not your wall clock. In live mode the time control shifts the domain (and the optional offset_t port can be wired to a scalar source to do the same thing from upstream). Hit stop on the node's play button and the values stop changing, because the node isn't generating new sample positions.
Then amplitude scales and offset shifts the result: default 1 and 0 keeps the −1..1 bipolar noise, amplitude 0.5 + offset 0.5 gives you 0..1 unipolar, which is what most Comfy widgets actually want. The three domain offsets are hash-domain offsets - they change which numbers you draw, they don't smooth or spatially organize anything. Non-finite values are scrubbed to zero on the way out, so you won't see NaN here.
The inputs and outputs that matter
- seed - INT 0..100000 (no randomize-after-generate control; it's fixed). Change it and you get a different deterministic sequence, same character.
- amplitude / offset - the range mapping. Defaults give −1..1.
- channels - INT 1..64. Unlike the oscillator, each channel here genuinely differs: the channel index is part of the hash, so four channels are four independent noise streams that line up in time.
- samples - samples per delivered block. The tooltip is the author's own and worth quoting: use
1for responsive scalar control;8at 60 Hz delivers 7.5 blocks a second. - sample_rate - samples per second, not blocks per second.
- time - the domain start for a queued or explicit evaluation.
- offset_x / offset_y / offset_z - optional FLOAT ports, defaults 0. All 0 unless you wire something in.
- channels / value out - the block, and its first channel's last sample. With one channel, that's your scalar.
Install
Everything in these pages ships in the genereti 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 line must use your ComfyUI's Python. Requirements are one frontend pin, comfyui-frontend-package>=1.53.6 - no models, no extra server, no torch work. Workflows → Genereti → ꘇ-Noise-Dimensions is the bundled demo for noise behaviour, and ꘇ-Performance-Timeline shows the project-time binding that drives it.
Common issues
It looks jittery when you wanted soft. That's the node. White noise has no correlation between samples, so anything downstream sees a nervous value. Smooth it with ꘇ chop.lag or run the expression node instead.
Nothing happens on Queue. Live generation uses the project clock; the queue uses the explicit time value. Both are deterministic, and neither reads your wall clock.
Sample rate isn't a frame rate. Setting sample_rate to 1000 does not give you 1000 updates per second in the browser - delivery is bounded by animation frames, roughly 60 blocks a second. Bigger blocks at a high rate give you high-rate signal content on a slower delivery cadence, which is exactly what samples is for.
Cycles are rejected. Feed the output back upstream and the live graph reports "CHOP cycles are not supported" rather than quietly building an unstable loop.
It's super alpha. The README's words: early classroom testing, back up workflows before updating, expect bugs and changing interfaces.
Inputs (11)
| Name | Type | Default | Description |
|---|---|---|---|
| seed | INT | 00–100000 | — |
| amplitude | FLOAT | 1.00-100000–100000 | — |
| offset | FLOAT | 0.00-100000–100000 | — |
| channels | INT | 11–64 | — |
| samples | INT | 11–4096 | Samples per delivered block. Live updates/s = sample_rate / samples, limited by browser frames. Use 1 for responsive scalar control; 8 at 60 Hz delivers 7.5 blocks/s. |
| sample_rate | FLOAT | 60.001–1000 | Samples per second, not blocks per second. A block contains samples values; use samples=1 for updates near this rate. |
| time | FLOAT | 0.00-100000–100000 | — |
| offset_xopt | FLOAT | 0.00-100000–100000 | — |
| offset_yopt | FLOAT | 0.00-100000–100000 | — |
| offset_zopt | FLOAT | 0.00-100000–100000 | — |
| offset_topt | FLOAT | 0.00-100000–100000 | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| channels | GENERETI_CHOP | — |
| value | FLOAT | — |