Nodes/ꘇ Genereti/ꘇ chop.oscin
ComfyUI Node

ꘇ chop.oscin

Get numbers in from TouchDesigner (same machine only)

By languel·Created about a month ago·Updated 3 days ago· 1
ꘇ chop.oscin
    • channels
    • value
    ◄port9000►
    ◄address*►

    ꘇ chop.oscin listens for OSC over UDP and turns the numbers in those messages into CHOP channels. This is the node that makes a two-app live setup plausible: TouchDesigner, Max, Pure Data, Resolume, a phone app, anything that speaks OSC pushes values and ComfyUI reacts without a single line of Python on your side.

    One hard constraint up front, because it decides whether this node is useful to you at all: the bridge binds to 127.0.0.1 and is loopback-only. Same machine, not your LAN. If your sender lives on another box, this node can't hear it.

    What it is

    It's the inbound twin of ꘇ chop.oscout, and it sits in the same conceptual slot as ꘇ chop.midiin: an input node whose values come from the outside world rather than from math. Downstream, nothing changes - ꘇ chop.select, ꘇ chop.math, ꘇ chop.switch and ordinary FLOAT sockets all treat it like any other signal.

    Context worth having: there's real, persistent demand for driving ComfyUI from realtime tools. Realtime/TouchDesigner threads show up regularly in r/StableDiffusion and r/comfyui, and they consistently hit the same wall - the exchange of control values between the visual-coding app and the diffusion graph. Genereti ships a small bridge for it rather than a full protocol implementation. Searching the reddit corpus for "genereti" itself returns zero threads, so there's no community playbook; the docs and the source are the whole story.

    How it works

    Connecting starts a UDP listener inside ComfyUI's Python server. Push ↔ Connect and the node POSTs to the pack's own route, which creates a datagram endpoint on your chosen port and hands the browser a session token. Messages arriving on the port are decoded, coalesced to roughly 60 Hz, and pushed to the connected browser tab, where each argument becomes a channel named address:index - so /td/level 0.42 becomes a channel under that address with one argument. The address widget is a whitespace-separated glob pattern (* and ? supported) matched against the incoming address before anything is stored, which is how you listen to one stream out of a busy sender.

    The decoder is deliberately narrow. Float and int arguments are fine, T/F become 1.0/0.0, and strings are rejected outright - the error is literally "CHOP OSC accepts numeric and boolean arguments only". Bundles work if they're immediate; scheduled timetags, blobs and remote destinations are outside this first pass. Each packet can carry at most 64 arguments, and a message can't be deeply nested or larger than 64 KB.

    Session hygiene is built in and mostly invisible: leases expire after 90 seconds without a renewal (the browser pings every 30), the last disconnect closes the port, and the server caps concurrent sessions at 64. Nothing listens until you connect, and it's worth repeating the pack's rule - opening or queueing a workflow never performs device I/O, and a queued run of this node returns a zero placeholder rather than a live read.

    The inputs and outputs that matter

    • port - INT default 9000, restricted to 1024..65535. Pick something free; a port already in use gives you the OS error text in the node status line.
    • address - STRING default *, a glob pattern over OSC addresses.
    • channels out - one sample per received argument, named by address and argument index. Idle until the first message arrives.
    • value out - FLOAT, the last sample of the first channel. For a single-knob sender that's the value you want, but selecting the channel explicitly is safer once more than one stream is flowing.

    Install

    Everything here ships in the genereti 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 your ComfyUI's Python. Requirements are just comfyui-frontend-package>=1.53.6 - OSC is handled by the server's own async stack, so there's no python-osc dependency and nothing to compile. Workflows → Genereti → ꘇ-OpenTouch-Operators-and-Lessons includes user-started MIDI and OSC nodes that stay silent until you press Connect.

    Common issues

    Your sender is on another machine. Then it won't work. Requests to the bridge are rejected unless they come from loopback, and inbound packets from a non-loopback peer are dropped. Move the sender onto the same host.

    Strings vanish. Sending an OSC string argument (/addr s "hello") raises inside the decoder. Pack labels and file paths over OSC are not supported - numbers and booleans only.

    The port never opens. A stale process holding the port, a port below 1024, or forgetting to press Connect are the three usual causes. The status line reports the OS error text when the bind fails.

    Losing values while backgrounded. Coalescing means you get the latest values, not every message; browsers also throttle background tabs. Don't use this for timing-critical sequencing.

    It's an alpha pack. The README is refreshingly frank: super alpha, early classroom testing, expect bugs and changing interfaces. For OSC specifically, the docs note the implementation has been exercised over real loopback UDP, but scheduled timetags and anything beyond immediate numeric messages are explicitly out of scope for this pass.

    CategoryGenereti / CHOP

    Inputs (2)

    NameTypeDefaultDescription
    portINT90001024–65535—
    addressSTRING*—

    Outputs (2)

    NameTypeDescription
    channelsGENERETI_CHOP—
    valueFLOAT—