osc.js
osc.js is a library for reading and writing Open Sound Control messages in JavaScript. It works in both Node.js and in a web browser.
Why osc.js?
There are several other OSC libraries available for JavaScript. However, most depend on Node.js-specific APIs. This means that they can't be run in a browser or on web-only platforms such as Chrome OS. osc.js uses only cross-platform APIs (TypedArrays
and DataView
), ensuring that it can run in any modern JavaScript environment.
osc.js is fast, comprehensive, fully spec-compliant, tested, modular, and provides a wide variety of optional transports for sending and receiving OSC data.
What Does it Do?
osc.js reads and writes OSC-formatted binary data into plain JavaScript objects. It provides adaptors for Node.js Buffer objects as well as standard ArrayBuffers.
The core of osc.js is transport agnostic. You can receive OSC data in whatever manner works best for your application: serial port APIs such as node-serialport or chrome.serial, socket APIs such as Node.js dgram or WebRTC data channels, WebSockets or binary XHR messages should all work. Connect osc.js up to your source of incoming/outgoing data, and you're all set. This approach is consistent with the design of Open Sound Control as a content format that is independent from its means of transport.
In addition to the low-level encoder/decoder functions, osc.js also provides a comprehensive set of transport objects, called Port
s, for use in standard browsers, Chrome Apps, and Node.js applications. These include:
Transport | Supported Platforms |
---|
UDP | Node.js, Chrome Apps |
Serial port | Node.js, Chrome Apps |
Web Sockets | Browsers, Node.js, Chrome Apps |
TCP | Node.js |
For stream-based protocols such as serial and TCP, osc.js will take care of SLIP framing for you.
Status
osc.js supports all OSC 1.0 and 1.1 required and optional types.
How it Works
osc.js consists of two distinct layers:
- The transports, which provide a simple EventEmitter-style API for sending an receiving OSC packets using a variety of transports such as UDP and Web Sockets.
- The underlying stateless API that provides functions for reading and writing OSC packets.
Examples
In-depth example osc.js applications for the browser, Node.js, and Chrome OS are available in the osc.js examples repository.
Web Sockets in the Browser
The osc.WebSocketPort
object supports sending and receiving
OSC messages over Web Sockets.
Options
Property | Description | Default Value |
---|
url | The Web Socket URL to connect to (required for clients) | none |
socket | A Web Socket instance to bind to (optional); if supplied, it is your job to configure and open it appropriately | none |
Sample Code
More code examples showing how osc.js can be used in browser-based, Node.js, and Chrome App applications can be found in the osc.js examples repository.
Including osc.js in your HTML page:
<!DOCTYPE html>
<html>
<head>
<title>osc.js Web Sockets</title>
<meta charset="UTF-8" />
<script src="bower_components/osc.js/dist/osc-browser.min.js"></script>
</head>
<body></body>
</html>
Creating an OSC Web Socket Port object:
var oscPort = new osc.WebSocketPort({
url: "ws://localhost:8081"
});
Listening for incoming OSC messages:
oscPort.on("message", function (oscMsg) {
console.log("An OSC message just arrived!", oscMsg);
});
Sending OSC messages:
oscPort.send({
address: "/carrier/frequency",
args: 440
});
Sending OSC bundles:
oscPort.send({
timeTag: osc.timeTag(60),
packets: [
{
address: "/carrier/frequency",
args: 440
},
{
address: "/carrier/amplitude"
args: 0.5
}
]
});
Using osc.js with Require.js
require.config({
paths: {
slip: "../bower_components/slip.js/dist/slip.min",
EventEmitter: "../bower_components/eventEmitter/EventEmitter.min",
long: "../bower_components/long/dist/Long.min",
osc: "../bower_components/osc.js/osc-module.min"
}
});
require(["osc"], function (osc) {
});
Web Sockets in Node.js
The osc.WebSocketPort
object supports sending and receiving
OSC messages over Web Sockets.
Options
Property | Description | Default Value |
---|
url | The Web Socket URL to connect to (required for clients) | none |
socket | A Web Socket instance to bind to (required for servers, optional for clients); if supplied, it is your job to configure and open it appropriately | none |
Sample Code
var osc = require("osc"),
http = require("http"),
WebSocket = require("ws");
var app = require("express").express(),
server = app.listen(8081);
app.use("/", express.static(__dirname + "/static"));
var wss = new WebSocket.Server({
server: server
});
wss.on("connection", function (socket) {
var socketPort = new osc.WebSocketPort({
socket: socket
});
socketPort.on("message", function (oscMsg) {
console.log("An OSC Message was received!", oscMsg);
});
});
UDP in Node.js
The osc.UDPPort
object supports the sending and receiving of
OSC messages over Node.js's UDP sockets. It also supports broadcast and multicast UDP.
Options
Property | Description | Default Value |
---|
localPort | The port to listen on | 57121 |
localAddress | The local address to bind to | "127.0.0.1" |
remotePort | The remote port to send messages to (optional) | none |
remoteAddress | The remote address to send messages to (optional) | none |
broadcast | A flag specifying if messages should be sent via UDP broadcast | false |
multicastTTL | The time to live (number of hops) for a multicast connection (optional) | none |
multicastMembership | An array of multicast addresses to join when listening for multicast messages (optional) | none |
socket | A raw dgram.Socket to use instead of osc.js creating one for you; if supplied, it is your job to configure and bind it appropriately | none |
Sample Code
var udpPort = new osc.UDPPort({
localAddress: "0.0.0.0",
localPort: 57121
});
udpPort.on("bundle", function (oscBundle) {
console.log("An OSC bundle just arrived!", oscBundle);
});
udpPort.open();
udpPort.send({
address: "/s_new",
args: ["default", 100]
}, "127.0.0.1", 57110);
Serial in a Chrome App
Including osc.js in your Chrome App page
<script src="../bower_components/osc.js/dist/osc-chromeapp.min.js"></script>
Defining the appropriate permissions in manifest.json
{
"name": "OSC.js Chrome App Demo",
"version": "1",
"manifest_version": 2,
"permissions": [
"serial"
],
"app": {
"background": {
"scripts": ["js/launch.js"],
"transient": true
}
}
}
Connecting to the serial port and listening for OSC messages
var serialPort = new osc.SerialPort({
devicePath: "/dev/cu.usbmodem22131"
});
serialPort.on("message", function (oscMsg) {
console.log("An OSC message was received!", oscMsg);
});
serialPort.open();
UDP in a Chrome App
The osc.UDPPort
object supports the sending and receiving of
OSC messages over a chrome.sockets.udp
socket. It also supports broadcast and multicast UDP.
Options
Property | Description | Default Value |
---|
localPort | The port to listen on | 57121 |
localAddress | The local address to bind to | "127.0.0.1" |
remotePort | The remote port to send messages to (optional) | none |
remoteAddress | The remote address to send messages to (optional) | none |
broadcast | A flag specifying if messages should be sent via UDP broadcast | false |
multicastTTL | The time to live (number of hops) for a multicast connection (optional) | none |
multicastMembership | An array of multicast addresses to join when listening for multicast messages (optional) | none |
socketId | The id of an existing socket to use instead of osc.js creating one for you; if supplied, it is your job to configure and bind it appropriately | none |
The osc.js Low-Level API
There are two primary functions in osc.js used to read and write OSC data:
osc.readPacket()
, which takes a DataView-friendly data buffer (i.e. an ArrayBuffer, TypedArray, DataView, or Node.js Buffer) and returns a tree of JavaScript objects representing the messages and bundles that were readosc.writePacket()
, which takes a message or bundle object and packs it up into a Uint8Array or Buffer object
Both functions take an optional withMetadata
parameter, which specifies if the OSC type metadata should be included. By default, type metadata isn't included when reading packets, and is inferred automatically when writing packets.If you need greater precision in regards to the arguments in an OSC message, set the withMetadata
argument to true.
OSC Bundle and Message Objects
osc.js represents bundles and messages as (mostly) JSON-compatible objects. Here's how they are structured:
Messages
OSC Message objects consist of two properties, address
, which contains the URL-style address path and args
which is an array of either raw argument values or type-annotated Argument objects (depending on the value of withMetadata
when reading the message).
{
address: "/an/osc/address",
args: [
{}
]
}
Bundles
OSC bundle objects consist of a time tag and an array of packets
. Packets can be a mix of OSC bundle objects and message objects.
{
timeTag: {
},
packets: [
{}
]
}
Argument Objects with Type Metadata
Type-annotated argument objects contain two properties: type
, which contains the OSC type tag character (e.g. "i"
, "f"
, "t"
, etc.) and the raw value
.
{
type: "f",
value: 444.4
}
If you are using type-annotated arguments, you should also set the metadata
option to true
when you instantiate your OSCPort
instance (or in the options
argument to osc.writeMessage
if you're using the low-level API).
Time Tags
Time tag objects contain two different representations: the raw NTP time and the equivalent (though less precise) native JavaScript timestamp. NTP times consist of a pair of values in an array. The first value represents the number of seconds since January 1, 1900. The second value is a Uint32 value (i.e. between 0 and 4294967296) that represents fractions of a second.
JavaScript timestamps are represented as milliseconds since January 1, 1970, which is the same unit as is returned by calls to Date.now()
.
{
raw: [
3608146800,
2147483648
],
native: Number
}
Colours
Colours are automatically normalized to CSS 3 rgba values (i.e. the alpha channel is represented as a float from 0.0
to 1.0
).
{
r: 255,
g: 255,
b: 255,
a: 1.0
}
Mapping OSC to JS
Here are a few examples showing how OSC packets are mapped to plain JavaScript objects by osc.js.
Message | Objects |
---|
"/carrier/freq" ",f" 440.4 | {
address: "/carrier/freq",
args: [440.4]
}
|
"/float/andArray" ",f[ii]" 440.4 42 47 | {
address: "/carrier/freq",
args: [
440.4, [42, 47]
]
}
|
"/aTimeTag" ",t" 3608146800 2147483648 | {
address: "/scheduleAt",
args: [
{
raw: [3608146800, 2147483648],
jsTime: 1399158000500
}
]
}
|
"/blob" ",b" 0x63 0x61 0x74 0x21 |
{
address: "/blob",
args: [
Uint8Aray([0x63, 0x61, 0x74, 0x21])
]
}
|
"/colour" ",r" "255 255 255 255" | {
address: "/colour",
args: [{
r: 255,
g: 255,
b: 255,
a: 1.0
}
]
}
|
"/midiMessage" ",m" 0x00 0x90 0x45 0x65 | {
address: "/midiMessage",
args: [
// Port ID, Status, Data 1, Data 2
Uint8Array([0, 144, 69, 101])
]
}
|
License
osc.js is maintained by Colin Clark and distributed under the MIT and GPL 3 licenses.
Contributing to osc.js
Contributions and pull requests to osc.js are hugely appreciated. Wherever possible, all fixes and new features should be accompanied by unit tests to help verify that they work and avoid regressions. When new features are introduced, a pull request to the osc.js-examples repository with an example of how to use it is also appreciated.
Code should follow the style conventions of the project (such as they are), which can be automatically validated using JSHint by running grunt jshint
.
Currently, the project is maintained by one person; sometimes it will take a bit of time to respond, review, and merge contributions. Help with bug triage, code reviews, testing, and examples is also welcome.
How to Build and Test Your Contributions
osc.js depends on npm, bower, and Grunt. Make sure you have these installed, and then run the following commands to fetch all necessary dependencies:
npm install
grunt dedupe-infusion
bower install
To lint and generate builds from new source code:
grunt
Running unit tests:
- To run the Node.js unit tests, run
node tests/node-all-tests.js
- In the browser, open
tests/all-tests.html
Contributors
- @colinbdclark wrote the core.
- @jacoscaz and @xseignard fixed bugs.
- @egasmus added support for 64-bit integers.
- @heisters contributed fixes for broadcast and multicast UDP on Node.js.