Nodes/ꘇ Genereti/ꘇ chop.oscout
ComfyUI Node

ꘇ chop.oscout

Push your signal out to another app over UDP

By languel·Created about a month ago·Updated 3 days ago· 1
ꘇ chop.oscout
  • input
  • channels
  • value
◄port9001►
◄address/genereti►

ꘇ chop.oscout takes the latest value of each of your CHOP channels and sends them as float arguments in an OSC message. It's the outbound half of the pack's device story, and the direct opposite of a one-way pipeline: your oscillator, your LFO, your MIDI CCs and your audio-analysis bands all become UDP numbers that another application can read.

What it is

The modular-audio layer in ComfyUI is thin and self-contained - audio buses never leave the browser, and Genereti's own docs are careful to say the GENERETI_AUDIO_BUS wires carry routes, not PCM. Control values are the portable part. So OSC Out exists to translate a CHOP into something a lighting rig, a VJ app, or another creative-coding sketch can consume: latest control values at up to 30 Hz, one address, up to 64 arguments.

It's a legitimate escape hatch from the "my graph only exists inside ComfyUI" problem, and it's also a first pass. Don't plan a show around it without measuring on your own machine first.

How it works

Two things happen in order. The input block passes through to the node's outputs unchanged - the node is a tap, not a transformer, so it can sit mid-chain. Then, if a device session is connected, the last sample of each channel (up to 64 of them) is packed into a single OSC message: address first, then a float argument per channel, in channel order.

Sending has a few deliberate brakes. Updates are capped at roughly 30 a second, only one request is in flight at a time, and an identical payload is dropped rather than resent - so a value that stops moving stops generating traffic. The transport itself is a small UDP socket the Python server creates once and reuses; the packet is built and sent from the Comfy server process to 127.0.0.1 on your chosen port. Which means: the receiver has to be listening on the same machine. There's no host field to point at a second box, and that's a documented scope limit rather than an oversight.

Connecting is manual by design. Press ↔ Connect on the node; the browser starts an outbound session against the server's loopback bridge and holds it open with periodic renewals. Opening a workflow, or queueing one, never sends a packet. On Queue the node passes its input through and stops there.

The inputs and outputs that matter

  • input - the GENERETI_CHOP you're broadcasting. Feed it from ꘇ chop.oscillator, ꘇ chop.noise, ꘇ mod.analyze output, or anything else that produces named channels.
  • port - INT default 9001, range 1024..65535. Must match what your receiver is listening on; 9000 is the pack's OSC-in default, so 9001 is a deliberate non-clash.
  • address - STRING default /genereti. Must start with / and stay under 256 characters, or the packet is rejected with an explanatory error.
  • channels / value out - the passthrough block and its first channel's last sample.
  • There is no message-style dropdown here, unlike ꘇ chop.midiout: OSC messages are always float arguments, one per channel, in order. Sending 3 channels means your receiver needs to expect 3 arguments at that address.

One design consequence worth thinking through before you wire it: the channel order is the argument order, and nothing in the message labels them. If you add a channel upstream by merging another source in, you silently shift every argument's index. Keep the block shape stable while something downstream depends on it, or send a separate address per group.

Install

All of these operators ship in one pack. Manager → search genereti (title "ꘇ Genereti") → install → restart ComfyUI → hard-refresh the browser. 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 requirements file is a single frontend pin (comfyui-frontend-package>=1.53.6); the UDP work uses the server's own aiohttp/async stack, so there's nothing extra to install. Workflows → Genereti → ꘇ-OpenTouch-Operators-and-Lessons demonstrates the user-started OSC nodes, and ꘇ-Sound-Analysis is the easiest source of moving values to send.

Common issues

Nothing arrives at your receiver. Almost always one of: you haven't pressed Connect, the addresses don't match, or your receiver is bound to a different interface. It's loopback UDP - test with a listener on the same machine before blaming the sender.

Strings aren't supported. Only numeric and boolean arguments are encoded, and nothing else is sent. Addresses are the only strings in the packet.

Values are throttled and deduplicated. 30 Hz, one in flight, identical payloads dropped. Fast automation arrives as the latest value, not as a dense stream, and browser background throttling can stretch the gaps further.

Packets stop when the tab closes. Disconnect or leave the page and the session goes with it; a page-hide handler tells the server to release the lease rather than leaving a dangling listener.

Alpha. Super alpha, early classroom testing, per the README. Don't put it in a live performance without a rehearsal.

CategoryGenereti / CHOP

Inputs (3)

NameTypeDefaultDescription
inputGENERETI_CHOP—
portINT90011024–65535—
addressSTRING/genereti—

Outputs (2)

NameTypeDescription
channelsGENERETI_CHOP—
valueFLOAT—