A Node.js library for controlling Cockos Reaper via Open Sound Control (OSC).
npm install reaper-osc
# or
yarn add reaper-osc
You need to add an OSC control surface in Reaper before the library can communicate with it.
| Setting | Value |
|---|---|
| Mode | Configure device IP + local port |
| Device IP | IP address of the machine running your Node app |
| Device port | Port your app will listen on (e.g. 9000) |
| Local listen port | Port Reaper will listen on for commands (e.g. 8000) |
| Pattern config file | reaper-osc-js.ReaperOSC (copy from the patterns/ directory) |
The pattern file in patterns/reaper-osc-js.ReaperOSC configures what Reaper sends and receives. Copy it to Reaper's OSC patterns directory (usually <Reaper resource path>/OSC/), then select it in the surface settings. After copying or editing the file, click Edit → OK to reload it.
The ReaperConfiguration class holds all connection settings. Every property has a sensible default so you only need to set what differs from your setup.
import { Reaper, ReaperConfiguration } from 'reaper-osc';
const config = new ReaperConfiguration();
config.remoteAddress = '192.168.1.10'; // IP of the Reaper machine (default: 127.0.0.1)
config.remotePort = 8000; // Reaper's OSC listen port (default: 8000)
config.localPort = 9000; // Port this app listens on (default: 9000)
config.numberOfTracks = 8; // Tracks per bank (default: 8, must match pattern file)
config.numberOfMarkers = 8; // Markers per bank (default: 8)
config.numberOfRegions = 8; // Regions per bank (default: 8)
const reaper = new Reaper(config);
If you modify the number of tracks, markers, regions etc. you will need to modify your pattern file accordingly.
OSC is UDP, so there is no connection handshake Reaper does not automatically send state when your app starts. You have to ask for it:
await reaper.start();
// Ask Reaper to send the current values for all OSC messages
reaper.refreshControlSurfaces();
// Wait for the messages to arrive before reading state
setTimeout(() => {
console.log(reaper.tracks[0].name); // 'Drums'
console.log(reaper.transport.isPlaying); // false
}, 1000);
Reaper does broadcast its full state on startup.
The Reaper class is the main entry point. It maintains a stateful model of Reaper's transport, tracks, markers, and more, kept up to date as messages arrive.
// Playback
reaper.transport.play();
reaper.transport.stop();
reaper.transport.pause();
// Seek
reaper.transport.jumpToTime(30.5); // jump to 30.5 seconds
reaper.transport.jumpToTimeRelative(-5); // rewind 5 seconds from current position
// Loop
reaper.transport.setLoopStart(10.0);
reaper.transport.setLoopEnd(30.0);
reaper.transport.toggleRepeat();
// Markers and regions
reaper.transport.gotoMarker(2); // jump to user-assigned marker number 2
reaper.transport.gotoRegion(1); // jump to user-assigned region number 1
reaper.transport.toggleRewindByMarker();
// Read state
console.log(reaper.transport.time); // current position in seconds
console.log(reaper.transport.isPlaying);
console.log(reaper.transport.isRepeatEnabled);
console.log(reaper.transport.lastMarker.name);
reaper.tracks is a zero-indexed array of Track objects, one per bank slot. Bank slot 1 → reaper.tracks[0], bank slot 8 → reaper.tracks[7].
const track = reaper.tracks[0]; // bank slot 1
// Read state
console.log(track.name); // 'Vocals'
console.log(track.isMuted);
console.log(track.volumeDb);
// Commands
track.mute();
track.unmute();
track.solo();
track.unsolo();
track.setVolume(0.716); // fader position (0–1), ~0.716 = 0 dB
track.setVolumeDb(0);
track.setPan(-0.5); // -1 (full left) to +1 (full right)
track.setName('Lead Vox');
// FX
track.fx[0].bypass();
track.fx[0].nextPreset();
console.log(track.fx[0].name);
reaper.markers and reaper.regions are zero-indexed arrays of bank slots.
const marker = reaper.markers[0]; // first slot in the current marker bank
console.log(marker.name); // 'Verse 1'
console.log(marker.number); // '1' (user-assigned number, as a string)
console.log(marker.time); // 4.5 (seconds)
marker.rename('Intro');
const region = reaper.regions[0];
console.log(region.name);
console.log(region.length); // duration in seconds
region.rename('Part A');
region.setLength(16.0);
Every stateful object implements onPropertyChanged. The callback fires whenever that property changes, and returns an unsubscribe function.
// Log whenever track 1's mute state changes
const unsubscribe = reaper.tracks[0].onPropertyChanged('isMuted', () => {
console.log(`Track 1 muted: ${reaper.tracks[0].isMuted}`);
});
// Stop listening later
unsubscribe();
By default the library gives you 8 track slots matching Reaper's default bank size. Use reaper.device to navigate banks, which will trigger Reaper to send the state for the new bank:
// Move to the next bank of 8 tracks
reaper.device.nextTrackBank();
// Or jump directly to bank 2 (tracks 9–16 with a bank size of 8)
reaper.device.selectTrackBank(2);
The same bank navigation is available for markers and regions via reaper.device.nextMarkerBank(), reaper.device.selectMarkerBank(n), etc.
ReaperOscClient is the stateless layer underneath Reaper. It translates raw OSC messages into typed events and accepts typed commands. Use it directly if you need full control or want to handle Reaper events without the stateful model.
import { ReaperOscClient } from 'reaper-osc';
const client = new ReaperOscClient();
await client.start();
client.on('message', event => {
switch (event.type) {
case 'transport:play':
console.log('Playing:', event.playing);
break;
case 'track:mute':
console.log(`Track ${event.trackNumber} muted: ${event.muted}`);
break;
case 'track:volume':
console.log(`Track ${event.trackNumber} volume: ${event.volume}`);
break;
}
});
import { Play, Stop, SetTrackMute, SetTrackVolume } from 'reaper-osc';
client.send(Play());
client.send(Stop());
client.send(SetTrackMute(1, true)); // mute track 1
client.send(SetTrackVolume(1, 0.716)); // set track 1 volume (fader position)
For anything not covered by the typed commands, you can send a raw OSC message:
import { OscMessage } from 'reaper-osc';
// Trigger a Reaper action by command ID
client.sendRaw(new OscMessage('/action/str', [{ type: 's', value: '41743' }]));
Or via the Reaper class shorthand:
reaper.triggerAction(41743); // refresh control surfaces
reaper.triggerAction('_SWS_AWMPLAYREC'); // SWS extension actions work too
Full API documentation (all classes, properties, and methods) is at https://lykaiosnz.github.io/reaper-osc.js/.