Nodes/ꘇ Genereti/ꘇ chop.totop
ComfyUI Node

ꘇ chop.totop

Look at your signals as an image

By languel·Created about a month ago·Updated 3 days ago· 1
ꘇ chop.totop
  • input
  • image
◄layout▾►
◄width16►

ꘇ 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_CHOP to convert.
  • layout - channel_rows or rgba. Pick based on how many channels you have: 1..4 channels suggests rgba, more than four suggests channel_rows (or a ꘇ chop.select first).
  • width - INT default 16, 1..512. Only used by rgba; 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 rgba mode.

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.

CategoryGenereti / CHOP

Inputs (3)

NameTypeDefaultDescription
inputGENERETI_CHOP—
layoutCOMBO2 options: channel_rows, rgba
widthINT161–512—

Outputs (1)

NameTypeDescription
imageIMAGE—