Write Max objects in Python. PythonTap embeds CPython 3.13 in a Max package and runs a Python class as a Max object:
tap.python~runs it as an audio object: itsprocess()method is called on the signal — once per signal vector with numpy arrays, or once per sample with floats.tap.pythonruns it as an object without audio: messages in, messages out — and what a method returns is what the object outputs.
Either way, the class's type-annotated fields become Max attributes, its public methods become Max messages, the file is hot-reloaded every time you save it, and Python's print() and tracebacks land in the Max console — no exception your code raises can take Max down.
from attrs import define, field
@define
class mygain:
gain: float = field(default = 0.5)
def process(self, x: float) -> float:
return x * self.gain[tap.python~ mygain] ← loads python/mygain.py: a 'gain' attribute, process() on the signal
Read the book for everything else: how a class maps onto Max, audio and worker mode, objects without audio, performance, errors and limits, the examples, and building and testing it yourself.
Requires Max 9 or later, on macOS 11.0+ (Apple Silicon or Intel) or Windows 10 22H2+ / 11 (64-bit).
- Download a package from the releases page —
PythonTap-<version>.zipfor every platform at once, or the zip for yours. The Python runtime (CPython 3.13 withattrsandnumpy) is included. - Unzip it into your
Documents/Max 9/Packagesfolder. - Releases are not code-signed yet. On a Mac, clear the download's quarantine once, or Max will not load the external:
On Windows, SmartScreen may warn about the download.
xattr -dr com.apple.quarantine ~/Documents/"Max 9"/Packages/PythonTap
- Restart Max, and open PythonTap Overview from the Extras menu.
The book's Installing chapter says which zip is which, how to add Python packages to the runtime, and how to work from a clone of this repository.
- Save the class above as
mygain.pyin the package'spythonfolder (Documents/Max 9/Packages/PythonTap/python/). The file, the class and the object's argument share one name. - In a patcher:
[cycle~ 440]→[tap.python~ mygain]→[ezdac~]. Turn the audio on. - Send the object
gain 0.1, or give it@gain 0.1in its box, or connect anattrui. - Change
process()—return x * x * self.gain, say — and save: within a couple of seconds the object reloads, without a gap in the audio, keeping its gain.
For an object without audio, give the class a method that returns something: with def bang(self) -> float: return self.gain added, a bang to [tap.python mygain] outputs the gain. The package's python folder has more examples, and Max's Documentation window has the package's guide and three tutorials (Package Docs › PythonTap).
- The book — the whole documentation, and the measurements.
- CHANGELOG.md — what changed in each release.
- Building from source and testing, for contributors; the design and docs/PRODUCTION-PLAN.md, the roadmap.
MIT — see License.md, which also lists the third-party components (Min-API, CPython, attrs, NumPy) and where their licenses live.