XYZ OSC Control @ vrch.ai
XYZ OSC Control for when XY isn't enough
- X_INT
- Y_INT
- Z_INT
- X_FLOAT
- Y_FLOAT
- Z_FLOAT
- X_RAW
- Y_RAW
- Z_RAW
This is the same idea as the pack's XY OSC Control @ vrch.ai, just with a third axis bolted on. XYZ OSC Control @ vrch.ai listens for OSC messages and hands you three remapped values - X, Y, and Z - that you can wire into anything in your workflow. If you've got a joystick with twist, a phone's gyroscope, or a touch surface with three faders, this is the node that turns that hardware into a ComfyUI control surface.
Honestly, for most people XY is enough. The Z axis earns its keep when you actually have a three-degree input: a flight stick where twist is Z, a TouchOSC fader bank of three, or a motion-sensing device where orientation gives you three continuous values. It's the same OSC machinery, extended - so if you've already used the XY node, the only new thing here is the third channel and its wiring.
How it works
Identical to XY OSC Control, one axis up. The node binds a UDP listener on server_ip + port (default 8000) and watches the path (default /xyz). It accepts an OSC message to /xyz carrying three float arguments, or separate /xyz/x, /xyz/y, and /xyz/z messages with one argument each. Every axis goes through the same remap pipeline: input min/max (default 0.0–1.0) mapped onto output min/max (default 0–100), optional inversion, and an output_default fallback (50) used when no message has arrived yet.
The inputs and outputs that matter
The setup trio is the same as the XY node, and the defaults are again worth trusting:
server_ip- where the node listens. The baked-in default172.17.0.2is a Docker bridge address from the author's dev box; set your machine's LAN IP for phone control,127.0.0.1for same-machine testing.port(default8000) andpath(default/xyz) - must match what your OSC client sends.- The per-axis remap fields -
x/y/z_input_min/max,x/y/z_output_min/max,x/y/z_output_invert,x/y/z_output_default. Adjust the output range to fit whatever parameter you're driving (like 1–60 for steps or 0–10 for cfg).
Outputs mirror the XY node, three versions per axis: X_INT / Y_INT / Z_INT (remapped integers - wire into seed, cfg, steps and friends), X_FLOAT / Y_FLOAT / Z_FLOAT (remapped floats for things like denoise), and X_RAW / Y_RAW / Z_RAW (raw received values, great for confirming your client is sending what you think it is). A natural first project: three continuous inputs driving a camera's pan, tilt, and roll, or joystick X/Y plus twist mapped to controlnet strength, denoise, and seed.
How to install it
Same as every node in the VrchStudio/comfyui-web-viewer pack - no model files, nothing OSC-specific beyond the pack's own requirements. In ComfyUI Manager search "ComfyUI Web Viewer", or:
cd ComfyUI/custom_nodes
git clone https://github.com/VrchStudio/comfyui-web-viewer
cd comfyui-web-viewer
pip install -r requirements.txt
Windows portable installs use python_embeded\python.exe -m pip install -r ComfyUI\custom_nodes\comfyui-web-viewer\requirements.txt. Restart ComfyUI and you're set.
Common issues
Everything that bites the XY node bites this one: OSC is UDP, so a silent failure means no handshake to tell you about it. If your faders do nothing, check the same checklist - same network, server_ip set to the machine's real LAN IP, path and port matching on both ends, firewall not dropping UDP - and flip debug on to see exactly what the node is receiving. One listener per ip:port still applies, so give the XYZ node its own port or path if you're also running the XY version. One genuinely XYZ-flavored gotcha: if your client sends only two of the three values, the third axis just sits at its output_default - which is exactly why that default exists.
Inputs (22)
| Name | Type | Default | Description |
|---|---|---|---|
| server_ip | STRING | 172.17.0.2 | — |
| port | INT | 80000–65535 | — |
| path | STRING | /xyz | — |
| x_input_min | FLOAT | 0.00-9999–9999 | — |
| x_input_max | FLOAT | 1.00-9999–9999 | — |
| x_output_min | INT | 0-9999–9999 | — |
| x_output_max | INT | 100-9999–9999 | — |
| x_output_invert | BOOLEAN | false | — |
| x_output_default | INT | 50-9999–9999 | — |
| y_input_min | FLOAT | 0.00-9999–9999 | — |
| y_input_max | FLOAT | 1.00-9999–9999 | — |
| y_output_min | INT | 0-9999–9999 | — |
| y_output_max | INT | 100-9999–9999 | — |
| y_output_invert | BOOLEAN | false | — |
| y_output_default | INT | 50-9999–9999 | — |
| z_input_min | FLOAT | 0.00-9999–9999 | — |
| z_input_max | FLOAT | 1.00-9999–9999 | — |
| z_output_min | INT | 0-9999–9999 | — |
| z_output_max | INT | 100-9999–9999 | — |
| z_output_invert | BOOLEAN | false | — |
| z_output_default | INT | 50-9999–9999 | — |
| debug | BOOLEAN | false | — |
Outputs (9)
| Name | Type | Description |
|---|---|---|
| X_INT | INT | — |
| Y_INT | INT | — |
| Z_INT | INT | — |
| X_FLOAT | FLOAT | — |
| Y_FLOAT | FLOAT | — |
| Z_FLOAT | FLOAT | — |
| X_RAW | FLOAT | — |
| Y_RAW | FLOAT | — |
| Z_RAW | FLOAT | — |