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.
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¤t_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
- Open Sketch → Import Library → Manage Libraries
- Search for oscP5
- 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
- Find the sender machine’s local IP:
ip addr(Linux/macOS) oripconfig(Windows). - In the receiver’s
setup(), keepnew OscP5(this, 12000)the same. - In the sender’s
setup(), change theNetAddressto 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.Clienthandles raw TCP; for HTTPS APIs, use Java’sHttpURLConnectionin a background thread.parseJSONObject()andparseJSONArray()parse JSON strings intoJSONObject/JSONArraywithout external libraries.OscP5(this, port)creates a listener;NetAddress(ip, port)specifies the destination.- Every incoming message triggers
oscEvent(OscMessage msg)— checkmsg.checkAddrPattern()to route messages. - Send with
new OscMessage("/address").add(value)thenosc.send(msg, remote). - Normalise coordinates (divide by width/height) before sending so the receiver can scale them to any canvas size.