
Mapper device
The mapper device allows mapping the parameters between devices directly. It will operate permanently, even when the score is not running.
For the canonical language/runtime contract, see the QML device API (tree fields, value types, callbacks and Device) and QML transport API (Protocols, framing, HTTP and OAuth). This page covers device setup and worked mappings.
Setup and capabilities
Choose Mapper in Add device, then select a library QML definition or edit its script. The script returns an address tree from createTree(). Nodes can be containers or typed parameters; a parameter does not need a bind to exist. This makes a Mapper useful both for aliases/transforms and for script-managed intermediate values.
Mapper rebuilds bindings when other devices become available or reconnect. A missing source is not a source of live values: verify its device name and full address before relying on the mapping. Source updates change the Mapper’s value without automatically writing the same value back to the source. Explicit writes to the Mapper follow its write rule or binding.
Mapper is a persistent device, not a timeline process: stopping playback does not disable its script or its mappings. Stop or disconnect the relevant devices when you need to prevent hardware communication outside playback.
Libmapper is a separate protocol
Mapper is score’s QML/JavaScript mapping device. The optional LibmapperClient source integration is a distinct network protocol; it does not execute these QML scripts. See Libmapper client for its current build and backend limitations rather than assuming it is interchangeable with Mapper.
Script structure
Like other devices such as Serial, HTTP and WebSockets, it is defined in Javascript within a QML script. The basic form is:
import Ossia 1.0 as Ossia
Ossia.Mapper
{
function createTree() {
return [
{
name: "node",
children: [
{
name: "sensor"
}
]
}
]
}
}
This does nothing interesting and only creates a tree with a single address: Mapper:/node/sensor
Mapping a node of the mapper to another
This is useful to give for instance an user-readable name to another parameter. For instance, to give a name to a specific MIDI CC:
import Ossia 1.0 as Ossia
Ossia.Mapper
{
function createTree() {
return [
{
name: "node",
children: [
{
name: "sensor",
bind: "MidiDevice:/1/control/45",
type: Ossia.Type.Int
}
]
}
]
}
}
Which gives:

The mapping is bidirectional:
- When
MidiDevice:/1/control/45receives a message, it is written toMapper:/node/sensor - When
Mapper:/node/sensorreceives a message, it is written toMidiDevice:/1/control/45
Custom mappings with Javascript expressions
If one wants to transform the value, for instance to rescale it, it is possible to use small JS snippets:
import Ossia 1.0 as Ossia
Ossia.Mapper
{
function createTree() {
return [
{
name: "node",
children: [
{
name: "sensor",
bind: "MidiDevice:/1/control/45",
type: Ossia.Type.Float,
// What happens when the bound parameter (MidiDevice:/1/control/45) is written to:
//
// When MidiDevice:/1/control/45 receives the value 64,
// Mapper:/node/sensor will get the value 64 / 127, roughly 0.5.
read: function(orig, v) { return v.value / 127.; },
// What happens when the mapper parameter (Mapper:/node/sensor) is written to:
//
// When Mapper:/node/sensor receives the value 0.5,
// MidiDevice:/1/control/45 will get the value 0.5 * 127, roughly 64.
write: function(v) { return v.value * 127.; }
}
]
}
]
}
}
This example will scale the 0-127 integer values of the MIDI CC to 0-1 floating point values for the Mapper.
Example:

Binding to multiple parameters
bind: can be an array.
import Ossia 1.0 as Ossia
Ossia.Mapper
{
function createTree() {
return [
{
name: "node",
children: [
{
name: "sensor",
bind: ["MidiDevice:/1/control/45", "MidiDevice:/1/control/55"],
type: Ossia.Type.Float,
// The first parameter, `orig` is the OSC address of the parameter which
// was changed: it will be either "/1/control/45" or "/1/control/55".
// The second parameter is the value.
read: function(orig, v) { return v.value / 127.; },
// Here we now return an array of values, one for each address:
// for instance, if a message "0.5" is sent to Mapper:/node/sensor from within score,
// - MidiDevice:/1/control/45 will get 0.5 * 127
// - MidiDevice:/1/control/55 will get 0
write: function(v) { return [v.value * 127., 0]; }
}
]
}
]
}
}
Writing to arbitrary parameters
Sometimes one may want to map an address to another only known at run-time, depending on a message.
For instance, imagine a case where you want to send messages [channel, value] to control varying MIDI channels at run-time, e.g. sending the list message [12, 45, 127] to Mapper:/node/sensor should write the CC value 127 to the CC 45 on MIDI channel 12, e.g. at the address MidiDevice:/12/control/45.
This can be done by returning a list of address-value pairs from write:
[ { address: "foo:/bar", value: 123 }, etc... ]
In this case one must not set bind: or read: as they do not make sense:
Example:
import Ossia 1.0 as Ossia
Ossia.Mapper
{
function createTree() {
return [
{
name: "node",
children: [
{
name: "sensor",
type: Ossia.Type.List,
// What happens when the mapper parameter (Mapper:/node/sensor) is written to
write: (v) => {
// If v is [12, 45, 127], this gives:
// MidiDevice:/12/control/45
let addr = `MidiDevice:/${v.value[0].value}/control/${v.value[1].value}`
return [ { address: addr, value : v.value[2].value } ];
}
}
]
}
]
}
}
Mapping and combining values from multiple addresses
To do this, one can simply add a custom member to the QML object. For instance, here we combine two distinct addresses which represent an XY coordinate, in a single parameter of type Vec2.
import Ossia 1.0 as Ossia
Ossia.Mapper
{
// Our custom member which will contain the current value for the address.
property var xy: [0.0, 0.0]
function createTree() {
return [
{
name: "node",
children: [
{
name: "sensor",
bind: ["Millumin:/millumin/layer/x/instance", "Millumin:/millumin/layer/y/instance"],
type: Ossia.Type.Vec2f,
read: function(orig, v) {
// Assign to xy depending on the origin
if(orig === "/millumin/layer/x/instance")
xy[0] = v.value;
if(orig === "/millumin/layer/y/instance")
xy[1] = v.value;
return xy;
},
// Write to the correct addresses. "v.value" is a Vec2, so two floats directly
write: (v) => {
return [v.value[0], v.value[1]];
}
}
]
}
]
}
}
Using the mapper device as a generator
The device provides an easy way to create generic generative devices with Javascript.
Here is a simple example which creates a device which gives the time.
The interval: property is used to define at which granularity in milliseconds the parameters will be polled.
import Ossia 1.0 as Ossia
Ossia.Mapper
{
function createTree() {
return [
{
name: "hours",
type: Ossia.Type.Int,
interval: 1000, // The read function() will be called every 1000 millisecond (every second)
read: function() {
return new Date().getHours();
}
},
{
name: "minutes",
type: Ossia.Type.Int,
interval: 1000,
read: function() {
return new Date().getMinutes();
}
},
{
name: "seconds",
type: Ossia.Type.Int,
interval: 200,
read: function() {
return new Date().getSeconds();
}
}
];
}
}
Using the Device object
Use Device when incoming data or a timer must read or write parameters directly rather than through bindings. The Device object reference and examples cover local paths, cross-device addresses, tree edits and value conversions, including:
- Updating the Mapper’s own nodes.
- Writing to other devices and reading their current values.
- Combining reads and writes.
- Dispatching incoming OSC messages.
Using the Protocols object
For devices whose APIs need custom transports, keep a raw socket in a QML property and translate its messages into device values. The QML transport API contains the complete configuration and examples for UDP, TCP, WebSockets, Unix sockets, HTTP, OSC parsing, MIDI and serial.
Reference
The complete API reference is under Reference → Languages:
- Context objects and Device operations.
- Tree node properties and value types.
- Mapper callback signatures.
- Transport factories and socket callbacks.
Go directly to a relocated section:
- Writing to the mapper’s own nodes
- Writing to other devices
- Reading from other devices
- Combining Device reads and writes
- Reacting to incoming network data and dispatching to multiple devices
- UDP sockets
- TCP sockets
- WebSockets
- Unix sockets (Linux / macOS)
- HTTP requests
- OSC message parsing
- MIDI
- Common socket callbacks
- Available context objects
- Device object
- Protocols object
- Socket functions
- MIDI functions
- Other functions: HTTP requests and OSC parsing
- Tree node properties
-
Value types (
Ossia.Type) - Callback signatures