Which library do you recommend to add OSC communication to p5js?

Hello,

I want to add Open Sound Control (OSC) communication to my p5js sketches. Which library do you recommend for this purpose?

Thank you.

Do you need more than this: https://p5js.org/reference/#/p5.TriOsc

Open Sound Control is not about creating sound waves with oscillators. It’s about sending messages from computer to computer.

I couldn’t find a solution without using Node.js. I have node-osc functioning now. I couldn’t make it to run so I looked for some help from IAs. ChatGPT was useless but Claude 3 (Sonnet) was really useful and explained things quite well.

right, there’s no way to send UDP directly from the browser front-end. Sometimes I try to connect directly through websocket (for example running a websocket server on Processing). But this doesn’t work if you already have a toolchain of OSC like TouchOSC. I know osc.js tries to bridge them by using OSC protocol on websocket, but still you need node.js in between.

Actually I managed to get OSC messages but not to send them because I can’t make p5js to include the node-osc library into the sketch. The simple use of “import” or “include” is actually quite complicated and Claude was of no help there. How do you include node modules in your sketches?

1 Like

I don’t understand the link with the question…

I can run the osc lib from node.js and I could run the example to receive OSC messages into a webpage but I can’t manage to use the lib to send messages from a webpage. Anyone has managed to do it?

Can you post the code? (Node.js and front end)
Otherwise it’s impossible for others to debug it… :cry:

I think the issue is not my code but the library. As I understand, it can only send tcp and not udp and my receiver requires udp. I have requested some clarification from the developer of the osc lib.

I’m glad you are advancing but can you please share some more details, like the code? Otherwise other people have to do a detective job (set up node project with node-osc and try to replicate the problem). Also it would be valuable in the future when someone stumbles upon the same issue. If you are, for example, opening a github issue on the repo, sharing that link here would help a lot, too. Thanks!

I am absolutely not familiar with github versioning and store all my files locally. I understand it’s important to post code here to get help but I think that in this case the issue was in the library. I am now trying to use node-osc instead because it uses the dgram package and is therefore UDP-compatible for sure. I will post my code, either if I stumble on another issue or if I manage to make it run because I think this kind of association between p5js and node libraries should be more accessible to newbies.

So finally, I have managed to make it with node-osc.

My ignorance of some matters led me to seek some help from AI. Again Claude was quite clear but after some requests you have to wait for a few hours so I finished the task with ChatGPT.

I made a simple sketch which sends an OSC message at startup and other ones every time the user clicks the mouse button. Any IP address and port can be selected.

With the command line/terminal, you must go to the folder where the server is, start it with “node .” or “node server.js”. Then open the index.html in the browser.

Here is the server code:

const cors = require("cors");
const express = require("express");
const bodyParser = require("body-parser");
const osc = require("node-osc");

const app = express();
app.use(cors()); // Prevents cross-origin error messages
app.use(bodyParser.json());

// Error handling
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).send("Something went wrong!");
});

app.post("/send-osc", (req, res) => {
  const { address, args, remoteAddress, remotePort } = req.body;

  // Validate args is an array
  if (!Array.isArray(args)) {
    return res
      .status(400)
      .json({ success: false, error: "Invalid args, should be an array" });
  }

  // Checks port number otherwise sends a message in console
  const port = parseInt(remotePort, 10);
  if (isNaN(port) || port <= 0 || port >= 65536) {
    return res.status(400).json({ success: false, error: "Invalid port" });
  }

  const oscClient = new osc.Client(remoteAddress, port);

  const message = new osc.Message(address);
  args.forEach((arg) => message.append(arg));

  oscClient.send(message, (err) => {
    if (err) {
      console.error("Error while sending OSC message:", err);
      res.status(500).json({ success: false, error: err.message });
    } else {
      console.log("OSC message sent:", address, args);
      res.json({ success: true });
    }
  });
});

const PORT = 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

Here is the client.js code:

// sendOSCMessage - must be on top
async function sendOSCMessage(address, args, remoteAddress, remotePort) {
  try {
    const response = await fetch("http://localhost:3000/send-osc", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        address,
        args,
        remoteAddress,
        remotePort,
      }),
    });

    const data = await response.json();
    if (data.success) {
      console.log("OSC message sent successfully");
    } else {
      console.error("Failed to send OSC message:", data.error);
    }
  } catch (error) {
    console.error("Error sending OSC message:", error);
  }
}

function setup() {
  createCanvas(windowWidth, windowHeight);
  background(200);
  textAlign(CENTER, CENTER);
  textSize(32);
  fill(0);
  text("Click to send an OSC message", width / 2, height / 2);
  sendOSCMessage(
    "/message",
    [
      "You are going to receive an OSC message each time the mouse is cliked on the webpage",
    ],
    "169.254.100.100",
    5000
  );
}

function draw() {
  // add the usual p5js code you want here
}

// mousePressed p5js function to handle mouse clics
function mousePressed() {
  // random number sent upon every mouse click
  sendOSCMessage("/test -message", [Math.random()], "169.254.100.100", 5000);
}

Here is the index.html code:

<!DOCTYPE html>
 <html>
 <head>
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <!-- PLEASE NO CHANGES BELOW THIS LINE (UNTIL I SAY SO) -->
   <script language="javascript" type="text/javascript" src="libraries/p5.min.js"></script>
   <script language="javascript" type="text/javascript" src="client.cjs" defer></script>
   <!-- OK, YOU CAN MAKE CHANGES BELOW THIS LINE AGAIN -->
   <!-- This line removes any default padding and style.
        You might only need one of these values set. -->
  <style> body { padding: 0; margin: 0; } </style>
 </head>

 <body>
 </body>
</html>

Please note that there is a package.json file for both the server and the client (the latter being inside a “web” subfolder with index.hmtl, the libraries folder of p5js (not to be confused with the node-modules subfolder of Node.js.

The picture shows the structure of the project.

image

1 Like

Maybe you should also make package “express” serve “index.html” automatically: :wink:

const path = require('path');
app.use(express.static(path.join(__dirname, 'web')));
1 Like

@GoToLoop Can you explain?

Actually I can’t, b/c I dunno NodeJS yet. :melting_face:

That was a chat AI workaround it thrown me when I was just asking about how your code would automatically run “index.html”. :robot:

Only much later on I’ve realized your code didn’t do that all! :man_facepalming:

Instead, we’d need to manually run the “web/” subfolder separately from the server code. :globe_with_meridians:

BtW, your file “web/client.cjs” shouldn’t have extension “.cjs”, b/c it’s a regular JS file which doesn’t use require()!

On the other hand, the most adequate extension for “server.js” is “.cjs”:

1 Like

Glad that you figured out! A downside of this approach is that you can’t send data from the server to the browser in real time (of course, you can send data as a response to a POST request, but the server cannot anticipate when the request happens). One way to solve it (as an addition to the current code) is server-sent events. Or you can replace the communication with Websockets, on which you can perhaps find more tutorials than server-sent events.

Also, I suppose you are deploying it in a local network. The current code allows to send OSC messages to any port and any type of message specified by the browser.

Is it possible to use Websockets as they are built on tcp whereas OSC requires udp? Or the communication between the client and the server would be with Websockets but the server could still send over udp?

Yes. Basically you want to somehow pack the information in a Websocket message and then the server unpacks Websockets and packs into an OSC message. Since the conversion happens on the server code, TCP or UDP doesn’t matter.

osc.js, which I mentioned before, offers an API which makes the conversion coherent (so you can construct a Websocket message in a OSC style), but I feel this is not so necessary because after all OSC is not complicated. So you can just make your own wrapper. For Websockets, you can use the native Websockets (note that the browser offers WS but you need a third-party library for node.js) or, for example, socket.io.

1 Like