Processing Intermediate Article 10

Network & OSC

Use Processing's net library for HTTP requests and oscP5 to send and receive OSC messages between sketches, enabling real-time network collaboration.

⏱ 18 min network osc http oscP5 multi-sketch

Processing can communicate over networks using two complementary approaches: the built-in processing.net library for raw TCP/HTTP connections and JSON API calls, and the third-party oscP5 library for Open Sound Control (OSC), the lightweight protocol used throughout live performance, music, and interactive installations. This tutorial covers both and ends with two sketches that communicate via OSC over a local network.

HTTP Requests with processing.net

The processing.net library gives you a low-level Client class. For HTTP GET requests, craft the request manually:

import processing.net.*;

Client c;
String response = "";

void setup() {
  size(600, 400);
  // Connect to the server on port 80
  c = new Client(this, "api.open-meteo.com", 80);
  // Send a raw HTTP GET request
  c.write("GET /v1/forecast?latitude=51.5&longitude=-0.12&current_weather=true HTTP/1.1\r\n");
  c.write("Host: api.open-meteo.com\r\n");
  c.write("Connection: close\r\n");
  c.write("\r\n");
}

void draw() {
  background(30);
  if (c.available() > 0) {
    response += c.readString();
  }

  fill(200);
  textSize(12);
  textAlign(LEFT, TOP);
  text(response.length() > 0 ? response.substring(0, min(response.length(), 400)) : "Waiting...",
       20, 20, width - 40, height - 40);
}

Because the response may arrive over multiple frames, accumulate it into a String and parse once the connection closes (or after a marker like \r\n\r\n for the header/body boundary).

Parsing JSON from an API Response

Once you have the full response body, strip the HTTP headers and parse with parseJSONObject():

String extractBody(String raw) {
  int sep = raw.indexOf("\r\n\r\n");
  return sep >= 0 ? raw.substring(sep + 4) : raw;
}

void parseWeather(String body) {
  try {
    JSONObject root = parseJSONObject(body);
    JSONObject cw   = root.getJSONObject("current_weather");
    float temp      = cw.getFloat("temperature");
    float wind      = cw.getFloat("windspeed");
    println("Temp: " + temp + "°C  Wind: " + wind + " km/h");
  } catch (Exception e) {
    println("Parse error: " + e.getMessage());
  }
}

For modern HTTPS APIs, use a Thread with java.net.URL and java.net.HttpURLConnection (standard Java, always available in Processing) rather than processing.net.Client, since the built-in Client does not handle TLS.

void fetchHTTPS(String urlStr) {
  Thread t = new Thread(() -> {
    try {
      java.net.URL url = new java.net.URL(urlStr);
      java.net.HttpURLConnection conn =
        (java.net.HttpURLConnection) url.openConnection();
      conn.setRequestMethod("GET");
      java.util.Scanner sc = new java.util.Scanner(conn.getInputStream());
      StringBuilder sb = new StringBuilder();
      while (sc.hasNext()) sb.append(sc.nextLine());
      sc.close();
      // Parse on main thread for thread safety
      final String json = sb.toString();
      javax.swing.SwingUtilities.invokeLater(() -> parseWeather(json));
    } catch (Exception e) { e.printStackTrace(); }
  });
  t.start();
}

Installing oscP5

  1. Open Sketch → Import Library → Manage Libraries
  2. Search for oscP5
  3. Click Install (by Andreas Schlegel)

oscP5 works with Processing 3 and 4. The library page is at sojamo.de/libraries/oscP5.

OSC Fundamentals

OSC messages consist of an address pattern (a slash-prefixed string like /position) followed by typed arguments. The address acts like a URL — the receiver inspects it to decide what to do.

/position  floatX  floatY
/color     int:255 int:128 int:0
/trigger   string:"bang"

Addresses are arbitrary strings. Convention uses paths like /app/module/parameter.

Setting Up an OSC Receiver

import oscP5.*;
import netP5.*;

OscP5 oscP5;

void setup() {
  size(700, 500);
  // Listen on UDP port 12000
  oscP5 = new OscP5(this, 12000);
}

// This callback fires for every incoming OSC message
void oscEvent(OscMessage msg) {
  println("Address: " + msg.addrPattern());
  println("Type tag: " + msg.typetag());   // e.g. "ff" for two floats

  if (msg.checkAddrPattern("/position")) {
    float x = msg.get(0).floatValue();
    float y = msg.get(1).floatValue();
    println("Position: " + x + ", " + y);
  }
}

msg.typetag() is a string of type characters: f (float), i (int), s (string), b (blob). Use msg.get(index) to retrieve arguments by position.

Sending OSC Messages

import oscP5.*;
import netP5.*;

OscP5   oscP5;
NetAddress remote;

void setup() {
  size(700, 500);
  oscP5  = new OscP5(this, 9000);                   // our outgoing port
  remote = new NetAddress("127.0.0.1", 12000);       // destination IP and port
}

void draw() {
  // Build a message, add arguments, send
  OscMessage msg = new OscMessage("/position");
  msg.add(float(mouseX));
  msg.add(float(mouseY));
  oscP5.send(msg, remote);

  // Throttle to ~30 sends per second
  if (frameCount % 2 != 0) return;
}

To communicate between two computers on the same network, replace "127.0.0.1" with the receiver’s local IP address (e.g., "192.168.1.42"). Use 127.0.0.1 (localhost) when both sketches run on the same machine.

Full Example: Two Sketches, Mouse Position via OSC

Sketch A — Sender

This sketch sends the mouse position to port 12000 on localhost every frame. Run it in one Processing window.

import oscP5.*;
import netP5.*;

OscP5      osc;
NetAddress target;
int        lastSend = 0;

void setup() {
  size(500, 400);
  background(30, 40, 60);
  osc    = new OscP5(this, 9000);
  target = new NetAddress("127.0.0.1", 12000);
  textSize(14);
  fill(180, 200, 240);
  textAlign(CENTER, CENTER);
}

void draw() {
  background(30, 40, 60);

  // Crosshair at mouse
  stroke(100, 180, 255);
  strokeWeight(1);
  line(mouseX, 0, mouseX, height);
  line(0, mouseY, width, mouseY);

  noStroke();
  fill(100, 180, 255, 200);
  ellipse(mouseX, mouseY, 16, 16);

  fill(180, 200, 240);
  text("SENDER  →  move mouse", width / 2, height - 20);

  // Send at ~30 fps regardless of frameRate
  if (millis() - lastSend > 33) {
    OscMessage msg = new OscMessage("/mouse/position");
    msg.add(float(mouseX) / width);   // normalise to 0–1
    msg.add(float(mouseY) / height);
    osc.send(msg, target);
    lastSend = millis();
  }

  // Also send a click event
}

void mousePressed() {
  OscMessage msg = new OscMessage("/mouse/click");
  msg.add(float(mouseX) / width);
  msg.add(float(mouseY) / height);
  osc.send(msg, target);
}

Sketch B — Receiver / Visualiser

Run this in a second Processing window simultaneously.

import oscP5.*;
import netP5.*;

OscP5 osc;

// Received state
float rxX = 0.5, rxY = 0.5;        // normalised 0–1
ArrayList<PVector> trail = new ArrayList<PVector>();
ArrayList<PVector> clicks = new ArrayList<PVector>();
int MAX_TRAIL = 80;

void setup() {
  size(700, 500);
  osc = new OscP5(this, 12000);    // listen on port 12000
  colorMode(HSB, 360, 100, 100, 100);
  smooth();
}

void draw() {
  background(220, 15, 8);

  // Draw fading trail
  for (int i = 1; i < trail.size(); i++) {
    PVector a = trail.get(i - 1);
    PVector b = trail.get(i);
    float alpha = map(i, 0, trail.size(), 0, 90);
    float hue   = map(i, 0, trail.size(), 200, 320);
    stroke(hue, 80, 100, alpha);
    strokeWeight(map(i, 0, trail.size(), 1, 4));
    line(a.x, a.y, b.x, b.y);
  }

  // Draw click bursts
  noFill();
  for (int i = clicks.size() - 1; i >= 0; i--) {
    PVector c = clicks.get(i);
    c.z += 3;                         // use z as expanding radius
    float alpha = map(c.z, 0, 120, 80, 0);
    stroke(60, 90, 100, alpha);
    strokeWeight(2);
    ellipse(c.x, c.y, c.z, c.z);
    if (c.z > 120) clicks.remove(i);
  }

  // Cursor dot at received position
  float cx = rxX * width;
  float cy = rxY * height;
  noStroke();
  fill(280, 90, 100, 90);
  ellipse(cx, cy, 22, 22);
  fill(0, 0, 100, 80);
  ellipse(cx, cy, 8, 8);

  // HUD
  colorMode(RGB, 255);
  fill(200);
  textSize(13);
  textAlign(LEFT, TOP);
  text("RECEIVER  |  listening on :12000", 12, 12);
  text("OSC /mouse/position  x=" + nf(rxX,0,3) + "  y=" + nf(rxY,0,3), 12, 30);
  colorMode(HSB, 360, 100, 100, 100);
}

void oscEvent(OscMessage msg) {
  if (msg.checkAddrPattern("/mouse/position")) {
    rxX = msg.get(0).floatValue();
    rxY = msg.get(1).floatValue();

    float px = rxX * width;
    float py = rxY * height;
    trail.add(new PVector(px, py));
    if (trail.size() > MAX_TRAIL) trail.remove(0);
  }

  if (msg.checkAddrPattern("/mouse/click")) {
    float px = msg.get(0).floatValue() * width;
    float py = msg.get(1).floatValue() * height;
    clicks.add(new PVector(px, py, 0));
  }
}

Open both sketches in separate Processing windows. As you move the mouse in Sketch A, a glowing trail appears in Sketch B mirroring the movement. Clicking in Sketch A triggers an expanding ring in Sketch B.

Running on Two Different Computers

  1. Find the sender machine’s local IP: ip addr (Linux/macOS) or ipconfig (Windows).
  2. In the receiver’s setup(), keep new OscP5(this, 12000) the same.
  3. In the sender’s setup(), change the NetAddress to the receiver’s IP:
target = new NetAddress("192.168.1.55", 12000);

Ensure both machines are on the same network and no firewall blocks UDP port 12000.

Key Takeaways

  • processing.net.Client handles raw TCP; for HTTPS APIs, use Java’s HttpURLConnection in a background thread.
  • parseJSONObject() and parseJSONArray() parse JSON strings into JSONObject/JSONArray without external libraries.
  • OscP5(this, port) creates a listener; NetAddress(ip, port) specifies the destination.
  • Every incoming message triggers oscEvent(OscMessage msg) — check msg.checkAddrPattern() to route messages.
  • Send with new OscMessage("/address").add(value) then osc.send(msg, remote).
  • Normalise coordinates (divide by width/height) before sending so the receiver can scale them to any canvas size.