ꘇ chop.totop
Look at your signals as an image
- input
- image
ꘇ chop.totop turns a signal block into an IMAGE. Two layouts: each channel as a grayscale row, or the first four channels packed into RGBA pixels at a width you choose. It's the bridge that lets you see control data - and, more usefully, the bridge that lets a signal feed anything in ComfyUI that wants an image, including a TOP chain, an image preview, or a save node.
What it is
The pack is explicit that the six conversion directions between TOP, CHOP and DAT are boundaries, not magic. This is the CPU-to-texture one: data that lived in JavaScript typed arrays in your browser becomes a single-frame IMAGE tensor that the rest of ComfyUI can consume. The author's description calls it exactly that - "a CPU-data-to-texture upload boundary" - and the doc adds that data-to-texture converters upload a canvas and update at up to 10 Hz. That's the honest performance picture: this is for monitoring and visual signatures, not for piping 60 fps control data into a pixel path.
Where it earns its keep is diagnostics and vanity. ꘇ mod.analyze output as channel_rows gives you a live band readout in a Preview node. A 4-channel gesture block as rgba is a genuinely pretty motion trail you can save. And if you want to push signals through browser-side TOP operators - blur, displace, feedback, bloom - this is how they get in.
How it works
channel_rows walks the channels in order and stacks them: one channel per row, one sample per column, grayscale. The matrix goes through the shared image helper, which repeats it across R, G and B, attaches an alpha of 1, clamps everything to 0..1 and caps the result at 512×512. So a 3-channel block of 64 samples becomes a 64×3 image, clipped - and yes, if your signal swings negative, everything below zero reads as black rather than as a negative value. Non-finite values are scrubbed to zero first.
rgba takes the first four channels in order and interleaves them as the pixels of an image whose width you set; height is derived from the sample count rounded up. If the block has only three channels, alpha stays 1 for every pixel; with four channels, the fourth channel overwrites alpha. Note the asymmetry: channel_rows keeps every channel you give it, while rgba silently ignores anything past the fourth. Both paths clamp to 0..1 and produce one frame - a batch of one.
An empty input isn't an error; it produces a 1×1 black image. Sounds useless until you realise that's a decent placeholder that won't break a downstream chain while you're building it.
The inputs and outputs that matter
- input - the
GENERETI_CHOPto convert. - layout -
channel_rowsorrgba. Pick based on how many channels you have: 1..4 channels suggestsrgba, more than four suggestschannel_rows(or aꘇ chop.selectfirst). - width - INT default
16, 1..512. Only used byrgba; it's the pixel width before height is derived. Watch the total: the sample count multiplied out has to stay inside the 512×512 cap. - image out - a single-frame IMAGE. Wire it into a preview, a save node, or a TOP operator. Values clamp to 0..1 and the alpha is opaque unless you supplied a fourth channel in
rgbamode.
Install
One pack, all operators. 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 your ComfyUI's Python. Requirements are a single pin, comfyui-frontend-package>=1.53.6 - no models, no torch changes, no server. The bundled ꘇ-OpenTouch-Operators-and-Lessons workflow walks the conversion family end to end, and its lesson deliberately samples 8×8 at 2 Hz so that conversions don't slow a full-rate stage. That's the author telling you how to use these nodes: small and slow.
Common issues
Everything is black below zero. Both layouts clamp to 0..1, so bipolar signals lose their lower half. Shift them upstream - ꘇ chop.math with multiply 0.5 and add 0.5 gives you a 0..1 image where black no longer means "negative".
Only four channels appear. That's rgba. Use channel_rows when you have more, or select the ones you care about first.
It's not a live video feed. Data-to-texture uploads are documented at up to 10 Hz, on purpose. If you're feeding a TOP chain that expects real frames, expect visible stepping.
Big blocks get clipped. 512 is a hard cap in both directions, and the helper truncates rather than scaling. Keep sample counts modest - a few hundred at most - or select a subset of channels first.
Queue gives you one image. Live signals run in the browser; a queued run converts the explicit block the CHOP evaluated. If you queue a graph whose signal source is an oscillator, you'll get the same still frame every time.
Alpha pack. Super alpha, early classroom testing, per the README - back up workflows before updating.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| input | GENERETI_CHOP | — | |
| layout | COMBO | 2 options: channel_rows, rgba | |
| width | INT | 161–512 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |