Emerging Interfaces

OSC for Installations: Getting Started

How to get TouchDesigner, Max, Processing, a phone and a microcontroller all talking to each other — and the network details that decide whether your piece survives opening night.

Almost every installation ends up being more than one program. TouchDesigner drives the projection, Max handles the sound, a Python script reads a sensor, a phone acts as a remote. Open Sound Control is how you make them talk, and it’s the closest thing interactive art has to a universal glue.

1. What an OSC message is

An OSC message is an address plus arguments:

/light/1/brightness  0.75
/synth/note          60  0.9
/camera/blob/3       412  289  0.44

The address looks like a file path and works like one. The arguments are typed: floats, integers, strings, blobs.

That’s the whole format, and its two advantages over MIDI follow directly. Names instead of numbers/light/1/brightness rather than CC 74 on channel 3 — and real resolution: MIDI CC gives you 128 steps, OSC gives you a float. If you’ve ever heard a filter sweep step audibly, that’s 7-bit MIDI; OSC doesn’t do that.

2. UDP, and why it drops things

OSC almost always travels over UDP. UDP is fire-and-forget: it sends the packet and never checks whether it arrived. TCP, by contrast, guarantees delivery by retransmitting — and waits while it does.

For continuous data, UDP is the right choice, because a lost position update is irrelevant — another one arrives in 16 milliseconds. Waiting for a retransmission would add latency to every subsequent message, which is far worse.

But it has a consequence you must design around: never send state changes only once. If you send /scene/start a single time and that packet is dropped, your installation is silently in the wrong state for the rest of the day. Either send continuously (the current scene number, every frame) or acknowledge back. Continuous is simpler and almost always right for installations.

3. Addresses: design them like an API

Design the namespace before you write the patches. A layout that works:

/sensor/proximity/1     float
/sensor/proximity/2     float
/state/scene            int
/state/running          int
/audio/level/master     float

Three rules that save grief: group by source, not destination; keep names stable once other programs depend on them; and document the list somewhere — a text file beside the project — because in six months you won’t remember whether it’s /level or /volume.

4. A first setup, in three steps

Step one: two programs on one machine. Send from A to 127.0.0.1 on port 7000, receive in B on port 7000. 127.0.0.1 is the machine talking to itself, and it removes the network as a variable. Get this working before anything else.

Step two: a phone. Install TouchOSC or an equivalent, point it at your computer’s local IP address, and drive a parameter from a fader. This is the fastest way to confirm real network delivery, and gives you a remote control for testing.

Step three: two machines. Same thing with real IP addresses, which is where the problems start.

5. The network details that break installations

  • Hardcode IPs, don’t rely on DHCP. A router that reassigns an address overnight means a piece that doesn’t start in the morning. Static IPs on every machine, written on a label inside the rack.
  • Turn the firewall off or open the ports explicitly. macOS and Windows both block incoming UDP by default. This is the single most common reason “OSC isn’t working.”
  • Use a dedicated switch and cables, not the venue’s Wi-Fi. Gallery Wi-Fi is congested, out of your control, and will be worse on opening night with a hundred phones in the room. Wired, isolated, and no internet on that switch.
  • Don’t send at 1000Hz because you can. Rate-limit continuous senders to the frame rate of whatever consumes them; 60Hz is plenty. Flooding the network is a real failure mode.
  • Check for port collisions. Two programs can’t listen on the same port on the same machine.

6. Debugging

Get a monitor — Protokol, OSCmonitor, or a TouchDesigner OSC In DAT — and watch raw messages. Nine times out of ten the answer is immediately visible: wrong port, wrong address spelling, wrong type (an int where a float was expected), or nothing arriving at all, which means firewall.

Build every piece so it can be driven by hand from a monitor or a phone, independent of its sensors. When something misbehaves an hour before opening, you need to know whether it’s the sensor or the receiver, and being able to inject messages manually answers that in seconds.