Skip to main content

A library for offline generation of Max/MSP patcher (.maxpat) files.

Project description

py2max

Python 3.9+ License: MIT

A pure Python library for offline generation of Max/MSP patcher files (.maxpat, .maxhelp, .rbnopat).

If you are looking for Python 3 externals for Max/MSP, check out the py-js project.

Installation

pip install py2max

For the browser-based live editor and remote REPL, install the companion py2max-server package:

pip install py2max-server

For development:

git clone https://github.com/shakfu/py2max.git
cd py2max
uv sync
source .venv/bin/activate

Quick Start

from py2max import Patcher

p = Patcher('my-synth.maxpat')
osc = p.add('cycle~ 440')
gain = p.add('gain~')
dac = p.add('ezdac~')
p.link(osc, gain)
p.link(gain, dac)
p.save()

That's it! Open my-synth.maxpat in Max to see your patch.

Features

Core Capabilities

  • Offline Patch Generation - Create Max patches programmatically without Max running
  • Round-trip Conversion - Load, modify, and save existing .maxpat files
  • Max for Live (.amxd) - Read/write binary .amxd device files with presentation-mode helpers
  • Universal Object Support - Works with any Max/MSP/Jitter object
  • Fully typed - Passes mypy --strict; no runtime dependencies
  • High Test Coverage - 420+ tests ensure reliability

Max for Live (.amxd)

Generate Max for Live devices directly. Patcher.save() / Patcher.from_file() auto-detect the .amxd extension and read/write the binary device format, byte-for-byte compatible with Max-exported devices.

from py2max import Patcher

# device_type: "audio_effect" (default), "instrument", or "midi_effect"
p = Patcher('gain.amxd', device_type='audio_effect')
p.enable_presentation(devicewidth=120)        # render Ableton's device strip

plugin = p.add_textbox('plugin~')             # audio in from Live
gain = p.add('live.gain~', maxclass='live.gain~')
plugout = p.add_textbox('plugout~')           # audio back to Live
gain.add_to_presentation([20, 20, 60, 136])   # show the fader in the device

p.add_line(plugin, gain, outlet=0, inlet=0)
p.add_line(gain, plugout, outlet=0, inlet=0)
p.save()                                       # writes a binary .amxd

Helpers: Patcher.enable_presentation(devicewidth=...), Box.add_to_presentation([x, y, w, h]) (rejects M4L infrastructure objects and rounds fractional coordinates), and Patcher.enforce_integer_coords(). M4L binary helpers live in py2max.m4l.

Interactive Server (separate package)

Real-time browser-based patch editing with bidirectional sync lives in the companion py2max-server package, so the core library stays small and offline:

pip install py2max-server
py2max-server serve my-patch.maxpat
# Opens browser at http://localhost:8000

Features:

  • Drag objects, draw connections visually
  • Three layout engines: WebCola, ELK, and Dagre
  • Auto-save with debouncing
  • Navigate into subpatchers
  • REPL mode for Python interaction

SVG Preview

Generate high-quality SVG previews without Max:

py2max preview my-patch.maxpat --open
p = Patcher('synth.maxpat')
# ... add objects ...
p.to_svg('synth.svg', title="My Synth", show_ports=True)

Layout Managers

Five built-in layout strategies:

Layout Description
grid Connection-aware clustering with configurable flow
flow Signal flow-based hierarchical positioning
columnar Controls -> Generators -> Processors -> Outputs
matrix Signal chains in columns, categories in rows
horizontal/vertical Simple grid layouts
p = Patcher('patch.maxpat', layout='flow', flow_direction='vertical')
# Add objects and connections...
p.optimize_layout()  # Arrange based on signal flow
p.save()

MaxRef Integration

Access documentation for 1175 Max objects:

p = Patcher('demo.maxpat')
cycle = p.add('cycle~ 440')

print(cycle.help())  # Full documentation
print(f"Inlets: {cycle.get_inlet_count()}")
print(f"Outlets: {cycle.get_outlet_count()}")

Connection Validation

Optional validation catches wiring errors:

p = Patcher('patch.maxpat', validate_connections=True)
osc = p.add('cycle~ 440')
gain = p.add('gain~')

p.link(osc, gain)              # Valid
p.link(osc, gain, outlet=5)    # Raises InvalidConnectionError

Semantic IDs

Human-readable object IDs for easier debugging:

p = Patcher('patch.maxpat', semantic_ids=True)

osc1 = p.add('cycle~ 440')   # ID: 'cycle_1'
osc2 = p.add('cycle~ 220')   # ID: 'cycle_2'
gain = p.add('gain~')        # ID: 'gain_1'

# Find by semantic ID
osc = p.find_by_id('cycle_1')

SQLite Database

Query Max object metadata efficiently:

from py2max.maxref import MaxRefDB

db = MaxRefDB()  # Auto-cached on first use
print(len(db))   # 1175 objects

if 'cycle~' in db:
    info = db['cycle~']
    print(info['digest'])

# Search and filter
results = db.search('filter')
msp_objects = db.by_category('MSP')

Usage Examples

Basic Patch Creation

from py2max import Patcher

p = Patcher('my-patch.maxpat')
osc = p.add('cycle~ 440')
gain = p.add('gain~')
dac = p.add('ezdac~')

p.link(osc, gain)
p.link(gain, dac)
p.link(gain, dac, inlet=1)  # Stereo
p.save()

Loading and Modifying Patches

p = Patcher.from_file('existing.maxpat')

# Find and modify objects
for box in p.find_by_text('cycle~'):
    print(f"Found oscillator: {box.id}")

p.save_as('modified.maxpat')

Subpatchers

p = Patcher('main.maxpat')
sbox = p.add_subpatcher('p mysub')
sp = sbox.subpatcher

# Build the subpatcher
inlet = sp.add('inlet')
gain = sp.add('gain~')
outlet = sp.add('outlet')
sp.link(inlet, gain)
sp.link(gain, outlet)

# Connect in main patcher
osc = p.add('cycle~ 440')
dac = p.add('ezdac~')
p.link(osc, sbox)
p.link(sbox, dac)
p.save()

Gen Codebox

add_gen_codebox() adds a standalone gen.codebox~ object -- a complete gen patch in a single box that sits directly in a regular Max patcher (unlike the inner codebox~ from add_codebox(), which belongs inside a gen~/rnbo~ subpatcher). Inlet/outlet counts are derived automatically from the highest inN/outN references in the code:

p = Patcher('fbdelay.maxpat')

# 1 inlet (in1), 1 outlet (out1)
osc = p.add('cycle~ 440')
cb = p.add_gen_codebox('''
Param feedback(0.5, min=0.0, max=0.95);
History fb(0.0);
out1 = in1 + fb * feedback;
fb = out1;
''')
dac = p.add('ezdac~')
p.link(osc, cb)
p.link(cb, dac)
p.save()

# Or via the add() string shortcut (single-line / `;`-terminated code)
cb = p.add('gen.codebox~ out1 = in1 * 0.5;')

Object Search

p = Patcher.from_file('complex-patch.maxpat')

# Find by ID
obj = p.find_by_id('obj-5')

# Find by text content
oscillators = p.find_by_text('cycle~')

# Find by object type
messages = p.find_by_type('message')

Command Line Interface

Patch Management

# Create new patch from template
py2max new demo.maxpat --template stereo

# Show patch info
py2max info demo.maxpat

# Generate SVG preview
py2max preview demo.maxpat --open

# Optimize layout
py2max optimize demo.maxpat --layout flow

# Validate connections
py2max validate demo.maxpat

Interactive Server

Provided by the separate py2max-server package (pip install py2max-server):

# Start server with browser editing
py2max-server serve my-patch.maxpat

# With REPL in same terminal
py2max-server serve my-patch.maxpat --repl

MaxRef Database

# Show cache status
py2max db cache location

# Create category-specific database
py2max db create msp.db --category msp

# Search objects
py2max db search maxref.db "oscillator" -v

# Query specific object
py2max db query maxref.db cycle~ --json

Converters

# Convert .maxpat to Python code
py2max convert maxpat-to-python patch.maxpat output.py

# Lookup object documentation
py2max maxref cycle~ --json

Use Cases

  • Scripted patch generation - Automate repetitive patch creation
  • Batch processing - Modify multiple .maxpat files programmatically
  • Parametric patches - Generate variations from configuration files
  • Test generation - Create .maxhelp files during external development
  • Container population - Prepopulate coll, dict, table objects with data
  • Generative patching - Algorithmic patch creation
  • CI/CD integration - SVG previews for documentation and version control

Testing

make test        # Run all tests
make typecheck   # Type checking with mypy
make lint        # Linting with ruff
make docs        # Build documentation

Design Notes

The .maxpat JSON format maps directly to three Python classes:

  • Patcher - The patch container with boxes and patchlines
  • Box - Individual Max objects
  • Patchline - Connections between boxes

All classes are extendable via **kwargs, allowing any Max object configuration. The add_textbox() method handles most objects, with specialized methods (add_subpatcher(), add_coll(), etc.) for objects requiring extra configuration.

Caveats

  • Max doesn't refresh from file when open - close and reopen to see changes, or use py2max-server serve (from the separate py2max-server package) for live editing
  • For tilde variants, use the _tilde suffix: p.add_gen() vs p.add_gen_tilde()
  • API docs in progress - see CLAUDE.md for comprehensive usage

Examples

The tests/examples/ directory contains working, tested examples organized by topic (see its README):

  • quickstart/basic_patch.py - Simple oscillator patch
  • tutorial/signal_processing_chain.py - Complex audio processing chain
  • tutorial/generative_music.py - Generative music system with patterns
  • layout/grid_layout_examples.py - Grid layout with clustering
  • advanced/data_containers.py - Tables, collections, and dictionaries
  • api/patcher_api_examples.py - Patcher API reference examples

External usage:

Contributing

We welcome contributions! See CONTRIBUTING.md for guidelines.

git clone https://github.com/shakfu/py2max.git
cd py2max
uv sync
source .venv/bin/activate
make test  # Verify setup

License

MIT License. See LICENSE for details.

Credits

  • HOLA algorithm: Kieffer, Dwyer, Marriott, Wybrow (IEEE 2016)
  • NetworkX: Hagberg, Schult, Swart (SciPy 2008)
  • Graph drawing techniques: Gansner, Koutsofios, North, Vo (IEEE 1993)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

py2max-0.3.3.tar.gz (1.3 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

py2max-0.3.3-py3-none-any.whl (1.2 MB view details)

Uploaded Python 3

File details

Details for the file py2max-0.3.3.tar.gz.

File metadata

  • Download URL: py2max-0.3.3.tar.gz
  • Upload date:
  • Size: 1.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for py2max-0.3.3.tar.gz
Algorithm Hash digest
SHA256 92a8cd73a9615a96e641cc12eba900feccd260fcb0a1a9ee83a4c3aafb9f303c
MD5 89a102a9d91581bc453109fbd6d46aff
BLAKE2b-256 1b56cdf1c5ef290b3d996eeef7df575c63814be2775d82b978ccdb6e035b4f37

See more details on using hashes here.

File details

Details for the file py2max-0.3.3-py3-none-any.whl.

File metadata

  • Download URL: py2max-0.3.3-py3-none-any.whl
  • Upload date:
  • Size: 1.2 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for py2max-0.3.3-py3-none-any.whl
Algorithm Hash digest
SHA256 91f56ce8eb92704cccf1fbb81e470c3d04ad07321fa2ad89b718e15c693c6924
MD5 eca3dcd56d3eea2d551f8ccf795dd196
BLAKE2b-256 f84e8389ceaf6fe2e2c592dd25a97eedc6c3a82bdf37ad5ff76b85b057808cae

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page