iTranslated by AI
Python Live Coding Options Like TidalCycles/SuperCollider: Differences and Selection Guide
Goal of this Article
For those who are using TidalCycles + SuperCollider (SuperDirt), this article summarizes architectural differences and practical choices to help you avoid confusion when asking:
- "Can I do similar live coding with Python?"
- "What are the differences? Which one should I choose?"
These are notes from my own research while looking for ways to perform Python live coding.
Conclusion: 5 Main Approaches
Even when we say "doing Tidal-like things with Python," the location of the implementation differs.
-
Use a Python live coding environment (FoxDot / Sardine, etc.)
→ The "coding in Python from the start" route -
Keep Tidal and use Python as an external controller (send /ctrl)
→ The "don't discard Tidal's timing strength" route (Recommended) -
Send OSC directly from Python to SuperDirt (/dirt/play)
→ The "flexible but difficult timing design" route -
Use Python as a frontend for SuperCollider (scsynth) (Supriya / sc3nb, etc.)
→ The "build closer to the sound system" route -
DSP with Python alone (pyo, etc.)
→ The "standalone Python without depending on SuperCollider" route
Baseline: Tidal + SuperDirt Flow (Standard for Comparison)
The basics of Tidal are roughly like this:
The key here is timing.
- Triggers from Tidal to SuperDirt are sent as "look-ahead" transmissions using "bundles (with timestamps)", not just simple OSC messages.
- In other words, the clean playback is thanks to the design of "sending future events together and having the receiver align them accurately."
The nature of the Python approach changes depending on which part handles this "look-ahead scheduling."
1) Python Live Coding Environments: FoxDot / Sardine
What's the Difference?
- Imagine that Python becomes the "pattern language + clock" instead of Tidal.
- Sounds are (in most cases) generated on the SuperCollider side.
FoxDot (Python → SuperCollider)
Features:
- Write in Python to play SuperCollider.
- Comes with a dedicated IDE, offering the quickest path to a "live coding in Python" experience.
A basic example from the README:
d1 >> play("x-o-")
Sardine (Python Live Coding Framework)
Features:
- Aimed at "turning Python into an instrument," it handles SuperCollider (or MIDI/OSC) under the hood.
- Also designed with a strong emphasis on "time and tempo."
Who Is This For?
- People who want to "code in Python rather than Haskell."
- People who want to write algorithms using "Pythonic code" (which makes it easier to link directly with data processing and generation).
Important Considerations
- Syntax and conventions vary by tool (they are different from Tidal's mini-notation).
- Since the long-term maintenance status and ecosystem activity vary by project, it's recommended to check the update history before adoption.
2) Keeping Tidal and Using Python as an External Controller (Recommended)
What's the Difference?
- Leave the timing accuracy (look-ahead scheduling) to Tidal.
- Python focuses on "creating and sending values" and "handling external logic or UI."
Tidal can receive external input via OSC. By default, it receives /ctrl at 127.0.0.1:6010, allowing you to use keys and values like variables.
Tidal side (Using received values)
-- Use external control (float) called "hello" for speed
d1 $ sound "bd" # speed (cF 1 "hello")
Python side (Sending /ctrl)
# pip install python-osc
from pythonosc.udp_client import SimpleUDPClient
client = SimpleUDPClient("127.0.0.1", 6010)
client.send_message("/ctrl", ["hello", 0.4])
Who Is This For?
- People who "love Tidal's syntax and don't want to discard it."
- People who want to extend their performance using Python for:
- Randomness/probability
- External device input
- Machine learning output
- Images/sensors/networks
Benefits
- "Python × Tidal" coexistence with minimal cost.
- Less likely to run into timing issues (since Tidal handles it).
3) Direct OSC Transmission from Python to SuperDirt (/dirt/play)
What's the Difference?
- Without going through Tidal, Python sends triggers directly to SuperDirt.
- While it offers the most freedom, you will have to handle the "look-ahead scheduling" that Tidal used to perform yourself.
The trigger sent by Tidal looks something like this (the original packs name-value pairs into /dirt/play):
-
s bd(which sample) -
speed 2(parameter) - Additionally, things like
cps / cycle / delta / orbitare attached.
Minimal example of "playing one shot" in Python (Conceptual)
from pythonosc.udp_client import SimpleUDPClient
client = SimpleUDPClient("127.0.0.1", 57120) # SuperDirt listening port
# Sending name-value pairs to /dirt/play (compatible format)
client.send_message(
"/dirt/play",
[
"cps", 0.5,
"cycle", 0.0,
"delta", 0.25,
"orbit", 0,
"s", "bd",
"speed", 1.0
],
)
Who Is This For?
- People who want to create their own unique "pattern engine."
- People who only want the "control aspect" of triggering SuperDirt from an external system.
Important Considerations (The Biggest Pitfall)
- Using
time.sleep()to time events is prone to jitter (fluctuations). - For clean playback, the basic strategy is to adopt a design like Tidal's: look-ahead transmission using OSC bundles with timestamps, which the receiver then aligns.
4) Using Python as a Frontend for SuperCollider (scsynth): Supriya / sc3nb
What's the Difference?
- The goal is to manipulate the sound synthesis engine from Python, rather than focusing on "Tidal-like syntax."
- It is often used not only for real-time but also for non-real-time (score generation) purposes.
Representative examples:
- Supriya: Aimed at starting and controlling scsynth from Python, including building the equivalent of SynthDefs on the Python side.
- sc3nb: Aimed at making SuperCollider easier to handle from Python (especially Jupyter).
Who Is This For?
- People whose goals are "building acoustic systems with Python" or "research, visualization, and data-driven" applications, rather than live coding.
- People who want to directly link existing Python assets (numerical calculation, ML, visualization) to sound.
5) DSP with Python Alone: pyo
What's the Difference?
- Synthesis and processing are completed entirely within Python, without going through SuperCollider.
- While it can be played in a "live coding style," pattern languages like Tidal's would typically need to be custom-built.
Who Is This For?
- People who want to reduce dependencies or complete everything within Python.
- People whose main purpose is learning and experimenting with sound processing and synthesis algorithms.
Comparison Table (Quick Overview)
| Route | Sound Engine | Role of Python | Timing Design | Who Is This For? |
|---|---|---|---|---|
| 1) FoxDot / Sardine | Mainly SuperCollider | Pattern + Clock + Control | Handled by the tool | People who want to live code in Python |
| 2) Tidal + Python(/ctrl) | SuperCollider | External controller | Tidal handles it | People who want to extend Tidal |
| 3) Python → SuperDirt Direct | SuperCollider | Everything (Pattern & Clock) | Build it yourself | People who want a custom implementation |
| 4) Supriya / sc3nb | Centered on scsynth | Synthesis/Control/Generation | Design it yourself or library support | Sound system oriented |
| 5) pyo | Within Python | Synthesis/Processing/Control | Design it yourself | People who want a Python-only solution |
Minimal Check on macOS (Assuming Tidal/SC)
The starting point is to launch SuperDirt in SuperCollider and ensure that the logs show listening ... 57120.
SuperDirt.start;
When facing "no sound" issues, it's easier to start by troubleshooting here (checking if it's running or if the port is open).
Which One Should You Choose? (Practical Recommendations)
-
If you want to maintain Tidal's performance feel (timing accuracy) → 2) Tidal + Python (/ctrl)
This is the least likely to cause frustration and offers great extensibility. -
If you want to start live coding in Python from the get-go → 1) FoxDot / Sardine
Provides the fastest experience for performing using "Python syntax." -
If you want to build everything yourself or your main goal is external system integration → 3) Direct /dirt/play Transmission
However, if the timing design is overlooked, jitter will occur quickly.
Reference Links
- TidalCycles OSC (/ctrl, /dirt/play, Playback controllers)
- Start Tidal (Log where SuperDirt waits at 57120)
- SuperDirt class help (start(57120, ...), etc.)
- python-osc (SimpleUDPClient)
- FoxDot GitHub (Examples available in README)
- Sardine Documentation
- Supriya GitHub
- sc3nb GitHub
- pyo documentation
Discussion