OSC IN FULL
a name for every parameter, an address for every gesture, a network to carry it all.
An OSC message carries no numbered 7-bit status, but a readable address and full machine types: `/synth/osc/freq` followed by a 32-bit float. The parameter has a name, the value keeps its precision, and the whole thing travels over any binary transport, from local UDP to WebSocket across the internet.
This sheet describes OSC 1.0, the only truly universal version, section by section: the binary packet, the semantic address, the types, the temporal bundles, the transport, namespace discovery with OSCQuery, and the real ecosystem where OSC lives today, from captured gesture to stage integration. The MIDI IN FULL sheet is an implicit prerequisite: OSC is best understood against what it set out to surpass.
Get this card as a PDF — print-ready A4, continuous reading.
/
00
GENESIS
Berkeley, 1997: a protocol born for the network
OSC was born in 1997 at CNMAT, the Center for New Music and Audio Technologies at the University of California, Berkeley. Matt Wright and Adrian Freed presented it at the ICMC conference under a programmatic title: Open Sound Control: A New Protocol for Communicating with Sound Synthesizers.
The context was specific. CNMAT's research synthesizers ran on networked workstations, driven by captured gesture, real-time analysis, experimental controllers. MIDI, designed in 1983 to link keyboards over a 31,250-baud serial cable, could no longer keep up. Its 7-bit resolution flattened nuance, its fixed vocabulary, note, velocity, numbered control change, had no way to name an arbitrary synthesis parameter, and its serial transport knew nothing of the network.
OSC answered with three breaks. First, data are no longer 7-bit integers but full machine types: 32-bit integers, floats, strings, binary blobs. Second, a message is no longer an opaque status but a readable address, organized as a tree like a filesystem path: `/voices/3/filter/cutoff`. Third, OSC defines no transport of its own. It describes a packet format, and lets UDP, TCP or any binary channel carry it.
That last decision is the most radical. MIDI is a cable as much as a format; it specifies down to the electrical voltage on the DIN plug. OSC is only a format. It inherits, for free, everything the network can do: IP addressing, multicast, the low latency of UDP, the reliability of TCP, traversal of the internet. A gesture captured in Marseille can drive a synth in Tokyo with no change to the protocol.
The context was specific. CNMAT's research synthesizers ran on networked workstations, driven by captured gesture, real-time analysis, experimental controllers. MIDI, designed in 1983 to link keyboards over a 31,250-baud serial cable, could no longer keep up. Its 7-bit resolution flattened nuance, its fixed vocabulary, note, velocity, numbered control change, had no way to name an arbitrary synthesis parameter, and its serial transport knew nothing of the network.
OSC answered with three breaks. First, data are no longer 7-bit integers but full machine types: 32-bit integers, floats, strings, binary blobs. Second, a message is no longer an opaque status but a readable address, organized as a tree like a filesystem path: `/voices/3/filter/cutoff`. Third, OSC defines no transport of its own. It describes a packet format, and lets UDP, TCP or any binary channel carry it.
That last decision is the most radical. MIDI is a cable as much as a format; it specifies down to the electrical voltage on the DIN plug. OSC is only a format. It inherits, for free, everything the network can do: IP addressing, multicast, the low latency of UDP, the reliability of TCP, traversal of the internet. A gesture captured in Marseille can drive a synth in Tokyo with no change to the protocol.
midi is a cable as much as a format. osc is only a format, and inherits for free everything the network can do.
Diagram
/
01
THE PACKET
address pattern, type tag string, arguments: the binary anatomy
An OSC message is a sequence of bytes structured in three consecutive zones, all aligned on 4-byte boundaries.
The first zone is the OSC Address Pattern: an ASCII string beginning with `/`, terminated by a null byte, padded with null bytes up to the next multiple of 4. It is the destination address, for example `/synth/freq`.
The second zone is the OSC Type Tag String: a string beginning with a comma `,`, followed by one character per argument describing each one's type. `,f` announces a float; `,if` an integer then a float. It too ends with a null, padded to a multiple of 4. This zone is the key to decoding: without it, the receiver would not know how to read the bytes that follow.
The third zone holds the arguments, encoded in binary in the order announced by the type tag, each aligned on 4 bytes, big-endian, most significant byte first.
The systematic 4-byte alignment is not decorative. It guarantees that each field lands on a memory-word boundary, which makes decoding fast and predictable, with no copying or realignment. Big-endian is network byte order, the long-standing convention of network protocols.
Take `/synth/freq` with the float value `440.0`. The address is 11 characters; add a null and pad to 12 bytes. The type tag `,f` is 2 characters, null and pad to 4. The float 440.0 takes 4 bytes. The whole message is 20 bytes, with no explicit separator: the structure reads itself from the lengths and the trailing nulls.
The first zone is the OSC Address Pattern: an ASCII string beginning with `/`, terminated by a null byte, padded with null bytes up to the next multiple of 4. It is the destination address, for example `/synth/freq`.
The second zone is the OSC Type Tag String: a string beginning with a comma `,`, followed by one character per argument describing each one's type. `,f` announces a float; `,if` an integer then a float. It too ends with a null, padded to a multiple of 4. This zone is the key to decoding: without it, the receiver would not know how to read the bytes that follow.
The third zone holds the arguments, encoded in binary in the order announced by the type tag, each aligned on 4 bytes, big-endian, most significant byte first.
The systematic 4-byte alignment is not decorative. It guarantees that each field lands on a memory-word boundary, which makes decoding fast and predictable, with no copying or realignment. Big-endian is network byte order, the long-standing convention of network protocols.
Take `/synth/freq` with the float value `440.0`. The address is 11 characters; add a null and pad to 12 bytes. The type tag `,f` is 2 characters, null and pad to 4. The float 440.0 takes 4 bytes. The whole message is 20 bytes, with no explicit separator: the structure reads itself from the lengths and the trailing nulls.
without the type tag string, the argument bytes are unreadable. it is what tells the receiver how to decode what follows.
Diagram
/
02
THE ADDRESS
the semantic break: naming instead of numbering
This is where OSC parts most sharply from MIDI. A MIDI control change is addressed by a number from 0 to 127 whose meaning depends entirely on the device receiving it. An OSC message is addressed by a readable path: `/synth/osc1/freq`, `/mixer/track/3/gain`, `/sensor/hand/left/x`.
That path is an OSC Address Pattern, organized as a tree by the `/` separators, exactly like a filesystem. Each segment is a container; the final leaf is an OSC Method, the real point where a value acts. The receiver exposes an address space, the set of addresses it understands, and each message strikes one or more of its methods.
One or more: because a message's address need not be literal. It is a pattern, which can contain wildcards and strike several methods at once.
- `` matches any sequence of characters: `/synth//freq` strikes the frequency of every sub-module.
- `?` matches a single character: `/synth/osc?/freq` targets `osc1`, `osc2`, but not `osc10`.
- `[ ]` defines a set or range of characters: `/synth/osc[1-3]/freq`.
- `{ }` lists whole alternatives: `/synth/{osc1,lfo2}/freq`.
Matching happens on the server side, at reception time. A single message `/synth/*/level 0.0` can silence every voice of a synth. This semantic addressability transforms the protocol: instead of a dictionary of numbers to memorize, you read a structure. The namespace becomes living documentation of what the machine can do.
That path is an OSC Address Pattern, organized as a tree by the `/` separators, exactly like a filesystem. Each segment is a container; the final leaf is an OSC Method, the real point where a value acts. The receiver exposes an address space, the set of addresses it understands, and each message strikes one or more of its methods.
One or more: because a message's address need not be literal. It is a pattern, which can contain wildcards and strike several methods at once.
- `` matches any sequence of characters: `/synth//freq` strikes the frequency of every sub-module.
- `?` matches a single character: `/synth/osc?/freq` targets `osc1`, `osc2`, but not `osc10`.
- `[ ]` defines a set or range of characters: `/synth/osc[1-3]/freq`.
- `{ }` lists whole alternatives: `/synth/{osc1,lfo2}/freq`.
Matching happens on the server side, at reception time. A single message `/synth/*/level 0.0` can silence every voice of a synth. This semantic addressability transforms the protocol: instead of a dictionary of numbers to memorize, you read a structure. The namespace becomes living documentation of what the machine can do.
a midi control change is a number whose meaning depends on the machine. an osc address is a path that reads itself, and can strike several at once.
Diagram
/
03
THE TYPES
the type tag string: explicit typing, message by message
OSC never assumes the type of a value. Each argument is preceded, in the type tag string, by a character describing it. Four types form the foundation, present in every implementation.
- `i`: signed 32-bit integer, big-endian.
- `f`: 32-bit IEEE 754 float.
- `s`: OSC-string, null-terminated ASCII padded to a multiple of 4.
- `b`: OSC-blob, arbitrary binary data preceded by a 4-byte count, then padded to a multiple of 4.
The 1.0 spec also defines optional types, adopted unevenly across implementations. The most useful ones carry a value:
- `h`: 64-bit integer. `t`: 64-bit OSC-timetag. `d`: 64-bit double float. `S`: symbol, like a string but semantically distinct. `c`: ASCII character in 32 bits. `r`: 32-bit RGBA colour. `m`: a 4-byte MIDI message, port, status, data1, data2, that is, MIDI wrapped inside OSC.
Four types carry no argument bytes at all. Their value is the type itself.
- `T`: true. `F`: false. `N`: nil, the absence of a value. `I`: infinitum, often used as impulse, a trigger with no data.
That last group is elegant. Sending `/gate ,T` transmits a true boolean without consuming a single argument byte: the information sits entirely in the type tag. An event trigger, `/trigger ,I`, carries nothing but the fact that it happened.
Explicit typing has a direct consequence for precision. Where MIDI folds every continuous value into 7 bits, that is 128 steps, an OSC `f` carries a full float. A cutoff frequency, a fader position, a gesture coordinate keep their native sensor resolution all the way to the sound engine.
- `i`: signed 32-bit integer, big-endian.
- `f`: 32-bit IEEE 754 float.
- `s`: OSC-string, null-terminated ASCII padded to a multiple of 4.
- `b`: OSC-blob, arbitrary binary data preceded by a 4-byte count, then padded to a multiple of 4.
The 1.0 spec also defines optional types, adopted unevenly across implementations. The most useful ones carry a value:
- `h`: 64-bit integer. `t`: 64-bit OSC-timetag. `d`: 64-bit double float. `S`: symbol, like a string but semantically distinct. `c`: ASCII character in 32 bits. `r`: 32-bit RGBA colour. `m`: a 4-byte MIDI message, port, status, data1, data2, that is, MIDI wrapped inside OSC.
Four types carry no argument bytes at all. Their value is the type itself.
- `T`: true. `F`: false. `N`: nil, the absence of a value. `I`: infinitum, often used as impulse, a trigger with no data.
That last group is elegant. Sending `/gate ,T` transmits a true boolean without consuming a single argument byte: the information sits entirely in the type tag. An event trigger, `/trigger ,I`, carries nothing but the fact that it happened.
Explicit typing has a direct consequence for precision. Where MIDI folds every continuous value into 7 bits, that is 128 steps, an OSC `f` carries a full float. A cutoff frequency, a fader position, a gesture coordinate keep their native sensor resolution all the way to the sound engine.
four osc types carry no argument bytes: T, F, N, I. the value is the type itself. a boolean or a trigger fits entirely in the type tag.
Diagram
/
04
THE BUNDLES
the NTP timetag: ordering events in time
MIDI plays messages as soon as they arrive; tempo and sync live elsewhere, in the MIDI clock. OSC settles the question inside the protocol itself, with bundles.
A bundle is a container that groups several OSC messages, or other bundles, under a single time stamp. Its structure is strict: the string `#bundle` null-terminated over 8 bytes, then an OSC Time Tag over 8 bytes, then a succession of elements, each preceded by its size on 4 bytes.
The timetag follows the NTP format: 32 bits for the seconds elapsed since 1 January 1900, then 32 bits for the fractional part of the second. The 32-bit fraction gives a theoretical resolution of about 200 picoseconds. The meaning is unambiguous: the receiver does not play the bundle on arrival, it plays it at the instant given by the timetag. If that instant has already passed, it plays it immediately.
One timetag value is reserved: `0x0000000000000001`, that is 63 zero bits and a low bit set to one. It means immediately, play right now. It is the protocol's only sentinel value, and the default for most simple messages not wrapped in a dated bundle.
Two properties follow from this mechanism. First, scheduling: a sender can dispatch a burst of dated events ahead of time, and the receiver plays them at the right moment, which absorbs network jitter. Second, atomicity: all messages in one bundle share the same instant and must be handled as an indivisible group, which prevents a chord from arriving note after note.
A bundle is a container that groups several OSC messages, or other bundles, under a single time stamp. Its structure is strict: the string `#bundle` null-terminated over 8 bytes, then an OSC Time Tag over 8 bytes, then a succession of elements, each preceded by its size on 4 bytes.
The timetag follows the NTP format: 32 bits for the seconds elapsed since 1 January 1900, then 32 bits for the fractional part of the second. The 32-bit fraction gives a theoretical resolution of about 200 picoseconds. The meaning is unambiguous: the receiver does not play the bundle on arrival, it plays it at the instant given by the timetag. If that instant has already passed, it plays it immediately.
One timetag value is reserved: `0x0000000000000001`, that is 63 zero bits and a low bit set to one. It means immediately, play right now. It is the protocol's only sentinel value, and the default for most simple messages not wrapped in a dated bundle.
Two properties follow from this mechanism. First, scheduling: a sender can dispatch a burst of dated events ahead of time, and the receiver plays them at the right moment, which absorbs network jitter. Second, atomicity: all messages in one bundle share the same instant and must be handled as an indivisible group, which prevents a chord from arriving note after note.
a bundle is not played on arrival, but at the instant of its timetag. 0x...01 means 'right now'. it is the protocol's only sentinel value.
Diagram
/
05
THE TRANSPORT
UDP, TCP, SLIP, WebSocket, multicast: a format with no imposed channel
OSC describes a packet, not a channel. The spec speaks of OSC packets carried by some transport mechanism, and imposes none. This is what makes OSC both flexible and sometimes disorienting: two correct implementations may fail to talk if they do not share the same transport.
UDP is the default choice, almost universal in practice. A UDP datagram has a clean boundary, and an OSC packet fits inside it whole: one datagram equals one packet, with no ambiguity. UDP guarantees neither delivery nor order, but its latency is minimal and handshake-free. For real-time control, where a lost message is at once replaced by the next, that trade-off is almost always the right one.
TCP brings reliability and ordering, at the cost of higher latency and a connection to maintain. But TCP is a continuous byte stream, with no message boundaries. Each OSC packet must therefore be framed. Two methods coexist: prefix each packet with its 4-byte size, or use SLIP encoding (RFC 1055), which delimits packets with a dedicated marker byte, doubled on collision. The question of framing over a stream is exactly what the OSC 1.1 proposal set out to standardize in 2010, without ever being ratified; most implementations stayed on 1.0 and handle framing their own way.
WebSocket carries OSC into the browser, which opens up real-time web interfaces; RNBO's Raspberry Pi runner uses it natively, alongside UDP. Multicast finally lets one sender address a group of receivers in a single send, on a group address, useful for synchronizing several machines on stage.
The choice of transport is a slider between latency and reliability. UDP for gesture and continuous streams, where freshness matters more than completeness; TCP for critical commands that must not be lost, a preset load, a state change.
UDP is the default choice, almost universal in practice. A UDP datagram has a clean boundary, and an OSC packet fits inside it whole: one datagram equals one packet, with no ambiguity. UDP guarantees neither delivery nor order, but its latency is minimal and handshake-free. For real-time control, where a lost message is at once replaced by the next, that trade-off is almost always the right one.
TCP brings reliability and ordering, at the cost of higher latency and a connection to maintain. But TCP is a continuous byte stream, with no message boundaries. Each OSC packet must therefore be framed. Two methods coexist: prefix each packet with its 4-byte size, or use SLIP encoding (RFC 1055), which delimits packets with a dedicated marker byte, doubled on collision. The question of framing over a stream is exactly what the OSC 1.1 proposal set out to standardize in 2010, without ever being ratified; most implementations stayed on 1.0 and handle framing their own way.
WebSocket carries OSC into the browser, which opens up real-time web interfaces; RNBO's Raspberry Pi runner uses it natively, alongside UDP. Multicast finally lets one sender address a group of receivers in a single send, on a group address, useful for synchronizing several machines on stage.
The choice of transport is a slider between latency and reliability. UDP for gesture and continuous streams, where freshness matters more than completeness; TCP for critical commands that must not be lost, a preset load, a state change.
udp gives one datagram per packet, a clean boundary, minimal latency. tcp is a stream with no boundaries: each packet must be framed, by size prefix or by slip.
Diagram
/
06
OSCQUERY
making the namespace discoverable: OSC's missing piece
OSC has a blind spot. To drive a machine, you must know its addresses in advance. The protocol says nothing about discovery: no standard message asks 'which addresses do you understand, and of what type?'. In 1997, you read the synth's docs and typed the paths by hand.
OSCQuery fills that gap. Proposed around 2017 by Vidvox, maker of VDMX, with others from the real-time performance community, it is an extension, not a change to OSC. The idea: a server exposes its full address space over HTTP, as a JSON document. A simple GET request returns the tree of addresses, and for each one the OSC type, the current value, the allowed range, the unit, read or write access, sometimes a description.
The protocol thus runs on two channels. HTTP/JSON serves description and discovery, off the real-time path. Plain OSC, over UDP or WebSocket, serves the actual control, on the fast path. You query once to learn what to send, then send in pure OSC. Local-network discovery relies on mDNS, the Bonjour mechanism: servers announce themselves, clients list them with no configuration.
The clearest example today comes from Cycling '74. When an RNBO patch is exported to a Raspberry Pi, the OSCQuery runner automatically generates the namespace from the patch's named parameters and its inports and outports. The result is immediately browsable: `http://c74rpi.local:5678` returns the full JSON, and control happens on `osc.udp://...:1234`. You export a patch, and you get a self-describing hardware synth, without writing a single line of network mapping.
Max 9 generalizes the same logic on the desktop. Its integrated OSC automatically exposes every parameter object in the patch, addressable over OSC with no manual wiring. VDMX plays both roles, server and client, and even publishes an auto-generated web interface for each control surface. The conceptual kinship with MIDI 2.0 is clear: MIDI-CI's Property Exchange pursues the same goal, making a device introspectable, but by a different path. See the MIDI IN FULL sheet.
OSCQuery fills that gap. Proposed around 2017 by Vidvox, maker of VDMX, with others from the real-time performance community, it is an extension, not a change to OSC. The idea: a server exposes its full address space over HTTP, as a JSON document. A simple GET request returns the tree of addresses, and for each one the OSC type, the current value, the allowed range, the unit, read or write access, sometimes a description.
The protocol thus runs on two channels. HTTP/JSON serves description and discovery, off the real-time path. Plain OSC, over UDP or WebSocket, serves the actual control, on the fast path. You query once to learn what to send, then send in pure OSC. Local-network discovery relies on mDNS, the Bonjour mechanism: servers announce themselves, clients list them with no configuration.
The clearest example today comes from Cycling '74. When an RNBO patch is exported to a Raspberry Pi, the OSCQuery runner automatically generates the namespace from the patch's named parameters and its inports and outports. The result is immediately browsable: `http://c74rpi.local:5678` returns the full JSON, and control happens on `osc.udp://...:1234`. You export a patch, and you get a self-describing hardware synth, without writing a single line of network mapping.
Max 9 generalizes the same logic on the desktop. Its integrated OSC automatically exposes every parameter object in the patch, addressable over OSC with no manual wiring. VDMX plays both roles, server and client, and even publishes an auto-generated web interface for each control surface. The conceptual kinship with MIDI 2.0 is clear: MIDI-CI's Property Exchange pursues the same goal, making a device introspectable, but by a different path. See the MIDI IN FULL sheet.
osc alone requires knowing the addresses in advance. oscquery exposes the namespace over http/json: types, ranges, values. you query once, then control in pure osc.
Diagram
/
07
THE ECOSYSTEM
OSC against MIDI, gesture, the stage, and the libraries that carry it
OSC did not replace MIDI, and that was never the real goal. The two protocols coexist because they answer different questions. MIDI reigns where the musical event is discrete and the keyboard central: notes, velocities, sequence, integration in a DAW. OSC reigns where control is continuous, named, distributed: gesture, network, installation, high resolution.
Gesture is OSC's natural ground. An accelerometer, an inertial unit, camera tracking, motion capture produce high-rate streams of floats. MIDI cannot carry a smooth movement in its 7 bits; an OSC `f` transports it losslessly, under a speaking address like `/dancer/hand/left/accel/x`. Dance, mocap, interactive installations, stage sensors speak OSC for this reason.
On the stage and image side, the ecosystem is dense. TouchDesigner and Notch consume and emit OSC for generative video mapping. Resolume Arena and Avenue expose a full OSC namespace for driving clips and effects. ETC EOS-family lighting consoles publish an OSC Implementation that has become a de facto standard in theatre. TouchOSC, by Hexler, turns a tablet into a custom OSC control surface. On the DAW side, Bitwig is driven over OSC through controller scripts from its API, notably those of the DrivenByMoss suite. Notable in its absence: Ableton Live has no native OSC; you reach it through Max for Live bridges, such as AbletonOSC, or via the MIDI OSCQuery Helper that publishes a Live Set over OSC.
Beneath all this, libraries in nearly every language. In C, liblo (Steve Harris, LGPL) is the de facto standard, the base of many other tools. In C++, oscpack (Ross Bencina) is compact and cross-platform. On microcontrollers, the CNMAT OSC library for Arduino is written by Adrian Freed himself, co-author of OSC, which makes it canonical for hardware. In digital art, ofxOsc is openFrameworks' OSC addon, and Andreas Schlegel's oscP5 is Processing's. In Python, python-osc is pure and maintained. In JavaScript, Colin Clark's osc.js handles UDP and WebSocket, the latter opening the browser. On Swift, OSCKit is modern and async. In Unity, extOSC for general use and Keijiro Takahashi's OscJack for demanding real-time. Elsewhere, JavaOSC on the JVM, rosc in Rust.
The split is clear, and it should be read without dogma. MIDI for the note and the studio; OSC for gesture, network, stage and high resolution. Many devices speak both, and bridges abound. See the MIDI IN FULL sheet for the other side of this border.
Gesture is OSC's natural ground. An accelerometer, an inertial unit, camera tracking, motion capture produce high-rate streams of floats. MIDI cannot carry a smooth movement in its 7 bits; an OSC `f` transports it losslessly, under a speaking address like `/dancer/hand/left/accel/x`. Dance, mocap, interactive installations, stage sensors speak OSC for this reason.
On the stage and image side, the ecosystem is dense. TouchDesigner and Notch consume and emit OSC for generative video mapping. Resolume Arena and Avenue expose a full OSC namespace for driving clips and effects. ETC EOS-family lighting consoles publish an OSC Implementation that has become a de facto standard in theatre. TouchOSC, by Hexler, turns a tablet into a custom OSC control surface. On the DAW side, Bitwig is driven over OSC through controller scripts from its API, notably those of the DrivenByMoss suite. Notable in its absence: Ableton Live has no native OSC; you reach it through Max for Live bridges, such as AbletonOSC, or via the MIDI OSCQuery Helper that publishes a Live Set over OSC.
Beneath all this, libraries in nearly every language. In C, liblo (Steve Harris, LGPL) is the de facto standard, the base of many other tools. In C++, oscpack (Ross Bencina) is compact and cross-platform. On microcontrollers, the CNMAT OSC library for Arduino is written by Adrian Freed himself, co-author of OSC, which makes it canonical for hardware. In digital art, ofxOsc is openFrameworks' OSC addon, and Andreas Schlegel's oscP5 is Processing's. In Python, python-osc is pure and maintained. In JavaScript, Colin Clark's osc.js handles UDP and WebSocket, the latter opening the browser. On Swift, OSCKit is modern and async. In Unity, extOSC for general use and Keijiro Takahashi's OscJack for demanding real-time. Elsewhere, JavaOSC on the JVM, rosc in Rust.
The split is clear, and it should be read without dogma. MIDI for the note and the studio; OSC for gesture, network, stage and high resolution. Many devices speak both, and bridges abound. See the MIDI IN FULL sheet for the other side of this border.
osc did not replace midi. midi holds the note, the keyboard, the studio. osc holds gesture, network, stage, high resolution. most devices speak both.
Diagram
References
Specifications
- OpenSoundControl 1.0 Specification Matt Wright · 2002
- opensoundcontrol.org — official site — 1.0 / 1.1 specs, implementations, publications
- OSCQuery Proposal Vidvox — dynamic address-space discovery