diff --git a/packages/osc/README.md b/packages/osc/README.md
new file mode 100644
index 00000000..e8f5069b
--- /dev/null
+++ b/packages/osc/README.md
@@ -0,0 +1,33 @@
+# @strudel.cycles/osc
+
+OSC messaging between strudel and super collider?
+
+## Sniffing Tidal Messages
+
+```sh
+npm run tidal-sniffer
+```
+
+Now open a .tidal file and play something. There should be logs like:
+
+```log
+received: /dirt/play [
+ '_id_', '1',
+ 'cps', 0.5625,
+ 'cutoff', 100,
+ 'cycle', 724.1875,
+ 'delta', 0.11111068725585938,
+ 'orbit', 0,
+ 's', 'arpy'
+]
+```
+
+## Web Client + Server (WIP)
+
+```sh
+npm run client
+npm run server # another terminal
+```
+
+Then go to [http://localhost:4321](localhost:4321) and push the button.
+In your server terminal, there should be a log.
diff --git a/packages/osc/index.html b/packages/osc/index.html
new file mode 100644
index 00000000..0890caa1
--- /dev/null
+++ b/packages/osc/index.html
@@ -0,0 +1,22 @@
+
+
+
diff --git a/packages/osc/package-lock.json b/packages/osc/package-lock.json
new file mode 100644
index 00000000..33516920
--- /dev/null
+++ b/packages/osc/package-lock.json
@@ -0,0 +1,76 @@
+{
+ "name": "@strudel.cycles/osc",
+ "version": "0.0.1",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "@strudel.cycles/osc",
+ "version": "0.0.1",
+ "license": "GPL-3.0-or-later",
+ "dependencies": {
+ "osc-js": "^2.3.0"
+ }
+ },
+ "node_modules/isomorphic-ws": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz",
+ "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==",
+ "peerDependencies": {
+ "ws": "*"
+ }
+ },
+ "node_modules/osc-js": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/osc-js/-/osc-js-2.3.0.tgz",
+ "integrity": "sha512-P2Oy9tf8Z9lQw8JZeR62HNqbKdxj7Kqbsag+ImiJvyxPDReGMVt5LtZbMh/7Ve/wbYEGODkQdFAaLHFVkIlHPw==",
+ "dependencies": {
+ "isomorphic-ws": "4.0.1",
+ "ws": "8.5.0"
+ }
+ },
+ "node_modules/ws": {
+ "version": "8.5.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
+ "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ }
+ },
+ "dependencies": {
+ "isomorphic-ws": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz",
+ "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==",
+ "requires": {}
+ },
+ "osc-js": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/osc-js/-/osc-js-2.3.0.tgz",
+ "integrity": "sha512-P2Oy9tf8Z9lQw8JZeR62HNqbKdxj7Kqbsag+ImiJvyxPDReGMVt5LtZbMh/7Ve/wbYEGODkQdFAaLHFVkIlHPw==",
+ "requires": {
+ "isomorphic-ws": "4.0.1",
+ "ws": "8.5.0"
+ }
+ },
+ "ws": {
+ "version": "8.5.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
+ "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
+ "requires": {}
+ }
+ }
+}
diff --git a/packages/osc/package.json b/packages/osc/package.json
new file mode 100644
index 00000000..67a0cc76
--- /dev/null
+++ b/packages/osc/package.json
@@ -0,0 +1,32 @@
+{
+ "name": "@strudel.cycles/osc",
+ "version": "0.0.1",
+ "description": "OSC messaging for strudel",
+ "main": "osc.mjs",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "server": "node server.js",
+ "tidal-sniffer": "node tidal-sniffer.js",
+ "client": "npx serve -p 4321"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/tidalcycles/strudel.git"
+ },
+ "keywords": [
+ "tidalcycles",
+ "strudel",
+ "pattern",
+ "livecoding",
+ "algorave"
+ ],
+ "author": "Felix Roos ",
+ "license": "GPL-3.0-or-later",
+ "bugs": {
+ "url": "https://github.com/tidalcycles/strudel/issues"
+ },
+ "homepage": "https://github.com/tidalcycles/strudel#readme",
+ "dependencies": {
+ "osc-js": "^2.3.0"
+ }
+}
diff --git a/packages/osc/server.js b/packages/osc/server.js
new file mode 100644
index 00000000..0d5c6986
--- /dev/null
+++ b/packages/osc/server.js
@@ -0,0 +1,129 @@
+const OSC = require('osc-js');
+
+const config = {
+ receiver: 'ws', // @param {string} Where messages sent via 'send' method will be delivered to, 'ws' for Websocket clients, 'udp' for udp client
+ udpServer: {
+ host: 'localhost', // @param {string} Hostname of udp server to bind to
+ port: 57121, // @param {number} Port of udp client for messaging
+ // enabling the following line will receive tidal messages:
+ // port: 57120, // @param {number} Port of udp client for messaging
+ exclusive: false, // @param {boolean} Exclusive flag
+ },
+ udpClient: {
+ host: 'localhost', // @param {string} Hostname of udp client for messaging
+ port: 57120, // @param {number} Port of udp client for messaging
+ },
+ wsServer: {
+ host: 'localhost', // @param {string} Hostname of WebSocket server
+ port: 8080, // @param {number} Port of WebSocket server
+ },
+};
+const osc = new OSC({ plugin: new OSC.BridgePlugin(config) });
+
+osc.open(); // start a WebSocket server on port 8080
+
+console.log('osc client running on port', config.udpClient.port);
+console.log('osc server running on port', config.udpServer.port);
+console.log('websocket server running on port', config.wsServer.port);
+
+// listen for messages from the client
+osc.on('*', (m) => {
+ // forward message to supercollider??
+ const message = new OSC.Message(m.address, ...m.args);
+ osc.send(message); // will it even be received??
+ console.log('forward:', m.address, m.args);
+});
+
+/*
+example tidal messages:
+
+/*
+
+received: /dirt/play [
+ '_id_',
+ '1',
+ 'cps',
+ 0.5625,
+ 'cycle',
+ 503.5,
+ 'delta',
+ 0.8888888359069824,
+ 'orbit',
+ 0,
+ 's',
+ 'bd'
+]
+received: /dirt/play [
+ '_id_', '1',
+ 'cps', 0.5625,
+ 'cycle', 503.6666564941406,
+ 'delta', 0.592592716217041,
+ 'orbit', 0,
+ 's', 'hh'
+]
+received: /dirt/play [
+ '_id_',
+ '1',
+ 'cps',
+ 0.5625,
+ 'cycle',
+ 504,
+ 'delta',
+ 0.8888888359069824,
+ 'orbit',
+ 0,
+ 's',
+ 'bd'
+]
+received: /dirt/play [
+ '_id_',
+ '1',
+ 'cps',
+ 0.5625,
+ 'cycle',
+ 504,
+ 'delta',
+ 0.592592716217041,
+ 'orbit',
+ 0,
+ 's',
+ 'hh'
+]
+received: /dirt/play [
+ '_id_',
+ '1',
+ 'cps',
+ 0.5625,
+ 'cycle',
+ 504.3333435058594,
+ 'delta',
+ 0.5925922393798828,
+ 'orbit',
+ 0,
+ 's',
+ 'hh'
+]
+received: /dirt/play [
+ '_id_',
+ '1',
+ 'cps',
+ 0.5625,
+ 'cycle',
+ 504.5,
+ 'delta',
+ 0.8888888359069824,
+ 'orbit',
+ 0,
+ 's',
+ 'bd'
+]
+received: /dirt/play [
+ '_id_', '1',
+ 'cps', 0.5625,
+ 'cycle', 504.6666564941406,
+ 'delta', 0.592592716217041,
+ 'orbit', 0,
+ 's', 'hh'
+]
+
+*/
diff --git a/packages/osc/tidal-sniffer.js b/packages/osc/tidal-sniffer.js
new file mode 100644
index 00000000..a1bdd659
--- /dev/null
+++ b/packages/osc/tidal-sniffer.js
@@ -0,0 +1,125 @@
+const OSC = require('osc-js');
+
+const config = {
+ receiver: 'ws', // @param {string} Where messages sent via 'send' method will be delivered to, 'ws' for Websocket clients, 'udp' for udp client
+ udpServer: {
+ host: 'localhost', // @param {string} Hostname of udp server to bind to
+ port: 57120, // @param {number} Port of udp client for messaging
+ exclusive: false, // @param {boolean} Exclusive flag
+ },
+ udpClient: {
+ host: 'localhost', // @param {string} Hostname of udp client for messaging
+ // port: 57120, // @param {number} Port of udp client for messaging
+ port: 41235, // @param {number} Port of udp client for messaging
+ },
+ wsServer: {
+ host: 'localhost', // @param {string} Hostname of WebSocket server
+ port: 8080, // @param {number} Port of WebSocket server
+ },
+};
+const osc = new OSC({ plugin: new OSC.BridgePlugin(config) });
+
+osc.open(); // start a WebSocket server on port 8080
+
+console.log('osc client running on port', config.udpClient.port);
+console.log('osc server running on port', config.udpServer.port);
+console.log('websocket server running on port', config.wsServer.port);
+
+// listen for messages from the client
+osc.on('*', (m) => {
+ console.log('received:', m.address, m.args);
+});
+
+/*
+example tidal messages:
+
+/*
+
+received: /dirt/play [
+ '_id_',
+ '1',
+ 'cps',
+ 0.5625,
+ 'cycle',
+ 503.5,
+ 'delta',
+ 0.8888888359069824,
+ 'orbit',
+ 0,
+ 's',
+ 'bd'
+]
+received: /dirt/play [
+ '_id_', '1',
+ 'cps', 0.5625,
+ 'cycle', 503.6666564941406,
+ 'delta', 0.592592716217041,
+ 'orbit', 0,
+ 's', 'hh'
+]
+received: /dirt/play [
+ '_id_',
+ '1',
+ 'cps',
+ 0.5625,
+ 'cycle',
+ 504,
+ 'delta',
+ 0.8888888359069824,
+ 'orbit',
+ 0,
+ 's',
+ 'bd'
+]
+received: /dirt/play [
+ '_id_',
+ '1',
+ 'cps',
+ 0.5625,
+ 'cycle',
+ 504,
+ 'delta',
+ 0.592592716217041,
+ 'orbit',
+ 0,
+ 's',
+ 'hh'
+]
+received: /dirt/play [
+ '_id_',
+ '1',
+ 'cps',
+ 0.5625,
+ 'cycle',
+ 504.3333435058594,
+ 'delta',
+ 0.5925922393798828,
+ 'orbit',
+ 0,
+ 's',
+ 'hh'
+]
+received: /dirt/play [
+ '_id_',
+ '1',
+ 'cps',
+ 0.5625,
+ 'cycle',
+ 504.5,
+ 'delta',
+ 0.8888888359069824,
+ 'orbit',
+ 0,
+ 's',
+ 'bd'
+]
+received: /dirt/play [
+ '_id_', '1',
+ 'cps', 0.5625,
+ 'cycle', 504.6666564941406,
+ 'delta', 0.592592716217041,
+ 'orbit', 0,
+ 's', 'hh'
+]
+
+*/