Basic template for sending OSC data from Python (and receiving OSC data in Python)
Related to notes / Sending OSC messages from the command line
Install pyliblo3 :
pip install pyliblo3
start_listening() starts a server that listens for messages sent to the port (port 9000 by default).
The verbose argument is useful for debugging methods.
# basic_server.py
from pyliblo3 import ServerThread, send, make_method, Address
_server = None
# the address the client is listening on
processing = Address("localhost", 12000)
# starts the server; intended to be run from the REPL with start_listening()
def start_listening(port=9000, verbose=True):
global _server
class InteractiveServer(ServerThread):
# a method that responds to a path; in ths case "/complex/status"
@make_method("/complex/status", "ff")
def status_callback(self, path, args):
re, im = args
print(f"\u2190 Received status update, re: {re}, im: {im}")
# a fallback method that will respond to paths that haven't been caught
# by previous methods
@make_method(None, None)
def fallback(self, path, args):
print(f"\u2190 {path} {args}")
_server = InteractiveServer(port)
_server.start()
print(f"Listening on port {port}")
# send an OSC message to the client. e.g.:
# osc("/complex/number", -1.5, 0.001)
def osc(address, *values):
print(f"\u2192 {address} {values}")
send(processing, address, *values)
Running the server from the REPL #
Start the Python REPL in the same directory as the server code is in. E.g.:
❯ ipython
Import start_listening and osc:
Python 3.11.13 (main, Oct 7 2025, 15:34:32) [Clang 20.1.4 ]
Type 'copyright', 'credits' or 'license' for more information
IPython 9.9.0 -- An enhanced Interactive Python. Type '?' for help.
Tip: Use `%timeit` or `%%timeit`, and the `-r`, `-n`, and `-o` options to easily profile your code.
In [1]: from basic_server import start_listening, osc
In [2]: start_listening()
Listening on port 9000
In [3]: osc("/complex/number", -1.25, 0.001)
→ /complex/number (-1.25, 0.001)
Sending data to the server #
An example Processing app that uses oscP5 to send and receive OSC messages:
import oscP5.*;
import netP5.*;
Complex z;
Complex c;
float rePrev = 0.0;
float imPrev = 0.0;
OscP5 oscP5;
NetAddress myLocation;
float scaleAdjustment;
float zoom = 1.0;
boolean clearBg = false;
void setup() {
size(600, 600);
background(235);
oscP5 = new OscP5(this, 12000);
myLocation = new NetAddress("127.0.0.1", 9000);
scaleAdjustment = (width/2 - 10) / 4.0;
}
/*
* interesting points:
* -0.391+-0.587i (chaotic)
* -1.25+0.004i (takes a while to escape)
*/
void draw() {
translate(width/2, height/2);
scale(1, -1);
strokeWeight(4);
stroke(252, 3, 111);
if (clearBg) {
background(235);
clearBg = false;
}
if (z != null && z.magnitude() <= 8.0 && clearBg != true) {
float re = z.re * scaleAdjustment * zoom;
float im = z.im * scaleAdjustment * zoom;
strokeWeight(4);
point(re, im);
strokeWeight(1);
line(rePrev, imPrev, re, im);
rePrev = re;
imPrev = im;
z = z.mult(z).add(c);
}
}
void oscEvent(OscMessage message) {
println("### OSC message received:");
if (message.checkAddrPattern("/complex/number")) {
if (message.checkTypetag("ff")) {
clearBg = true;
float re = message.get(0).floatValue();
float im = message.get(1).floatValue();
c = new Complex(re, im);
z = new Complex(0.0, 0.0);
rePrev = 0;
imPrev = 0;
println("Updated z to (0.0, 0.0); Updated z to (" + re + ", " + im + ")" );
// Send a response back to Python, just to confirm that a response can be sent and received:
OscMessage statusMessage = new OscMessage("/complex/status");
statusMessage.add(re);
statusMessage.add(im);
oscP5.send(statusMessage, myLocation);
}
}
if (message.checkAddrPattern("/complex/zoom")) {
if (message.checkTypetag("f")) {
zoom = message.get(0).floatValue();
}
}
}
class Complex {
float re, im;
Complex(float re, float im) {
this.re = re;
this.im = im;
}
Complex add(Complex other) {
return new Complex(re + other.re, im + other.im);
}
Complex mult(Complex other) {
return new Complex(
re * other.re - im * other.im,
re * other.im + im * other.re
);
}
float magnitude() {
return sqrt(re*re + im*im);
}
}
Tags: