py2max-server
July 29, 2026 ยท View on GitHub
Browser-based live editor for py2max Max/MSP patches, with a Python console attached to the running patch.
This package was split out of the core py2max library so the patch generator stays a small, dependency-light, offline tool. Install this only when you want to edit patches in a browser.
Installation
pip install py2max-server
This pulls in py2max (the core generator, 0.4.1 or newer) and websockets. Python 3.9 or newer.
Upgrading from 0.2.x: the standalone terminal REPL is gone in 0.3.0.
py2max-server repland the--repl/--log-fileflags have been removed, along withReplClientand friends -- the console now lives in the editor itself. See the changelog.
Usage
py2max-server serve my-patch.maxpat
Opens a browser editor for the patch:
- HTTP server on
http://localhost:8000 - WebSocket on
ws://localhost:8001 - Python console on
ws://localhost:8002(used by the editor page itself)
Use --port to move all three, and --no-open to skip launching a browser.
Saving
Serving a patch does not modify it. Nothing is written until you ask:
- Save writes back to the file being served.
- Save As writes a copy elsewhere, through a browser file dialog.
--auto-saveopts into writing edits back automatically, a couple of seconds after each change.
Auto-save is off by default because an editor action you did not intend should not be able to reach disk.
Editing
- Drag objects to move them; rubber-band or shift-click to select several, then drag, resize or delete as a group.
- Select an object to get resize handles on its right edge, bottom edge and bottom-right corner. Any object can be resized, though it matters most for UI objects like
scope~,functionandlive.gain~; Max reasserts its own size for objects that are not resizable. - Drag from one port to another to connect; click a cord to select it.
- Double-click empty canvas to create an object, with type-ahead over common Max objects.
- Double-click an object to edit its text -- or, if it is a subpatcher, to descend into it. The breadcrumb walks back out, as does Parent and
Esc. Deleteremoves the selection. Undo/redo with Cmd/Ctrl-Z and Cmd/Ctrl-Shift-Z (or Cmd/Ctrl-Y), at any subpatcher depth.Ffits the patch to the window, the wheel zooms, and space-drag pans.- Open loads another
.maxpatfrom the server; Hide Comments removes comment boxes from view without deleting them.
There is no keyboard shortcut for saving -- use the Save button.
Layout
The sidebar offers three layout engines (ELK, Dagre, WebCola) with their parameters. Nothing moves until you press Apply Layout; Reset Layout returns objects to where they were when the patch loaded.
Straighten nudges objects sideways so patch cords run perpendicular, leaving vertical positions alone -- a tidy-up rather than a re-layout. Each object aligns to its primary feed, and a move that would collide with another object is skipped.
Port Placement switches between Max's own port spacing (anchored near the box corners) and an even spread along the edge.
Python console
Press Console in the toolbar for a Python prompt attached to the live patcher:
>>> len(p._boxes)
8
>>> osc = p.add_textbox('cycle~ 440')
>>> p.add_line(osc, p.find('gain~'))
>>> commands()
p is the Patcher. Edits appear on the canvas immediately; press Save to write them to disk. commands() lists the helpers (save, info, layout, optimize, clear, help_obj).
Tab completes against the live namespace -- p. attributes, the helpers, builtins -- inserting a unique match or extending to the common prefix and listing the candidates. Up/Down walk the history.
The console runs in the editor page, so there is no second terminal and no token to copy.
Python API
import asyncio
from py2max import Patcher
from py2max_server import serve_interactive
p = Patcher("demo.maxpat")
p.add("cycle~ 440")
async def main():
server = await serve_interactive(p, port=8000, auto_open=True)
await asyncio.sleep(60)
await server.stop()
asyncio.run(main())
serve_interactive starts the editor only. The console is a second server on
its own port, so add start_repl_server if you want the Console button to
connect -- the serve command does this for you:
from py2max_server import serve_interactive, start_repl_server
server = await serve_interactive(p, port=8000, auto_open=True)
console = await start_repl_server(p, server, port=8002) # editor expects port + 2
...
console.close()
await console.wait_closed()
await server.stop()
Pass auto_save=True to serve_interactive for the equivalent of --auto-save.
Security
The console executes code (eval/exec) sent over its socket. Every connection -- editor and console alike -- must present the session token the server generates at startup; unauthenticated connections are refused.
That token is embedded in the editor page, so anything able to fetch http://localhost:8000 can obtain it. Bind to localhost and treat the whole server as trusted-local-user-only. Do not expose these ports to a network.
Relationship to py2max
py2max (core) generates .maxpat files offline and has no knowledge of this server. py2max-server depends on py2max and operates on Patcher objects. Generating patches needs only py2max; editing them in a browser needs this package.
Development
make serve # serve examples/example.maxpat
make demo # serve a scratch copy, leaving the tracked example alone
make test
make qa # lint, format, typecheck, test