@tsln/amxd

v0.1.0npmsourcechangelog

Reads and writes Max for Live device files (.amxd) from Node: the binary container, and the patcher JSON inside it, which is the same structure a .maxpat holds.

import { readAmxd, writeAmxd } from "@tsln/amxd";

const device = readAmxd("Spat Web.amxd");
device.type; // "audio" | "instrument" | "midi"
device.patcher.patcher.boxes; // [{ box: { id, maxclass, text, ... } }, ...]

for (const { box } of device.patcher.patcher.boxes) {
  if (box.maxclass === "comment") box.text = box.text?.toUpperCase();
}
writeAmxd("Spat Web.amxd", device);

decode(bytes) and encode(device) do the same on a Uint8Array, for when the file comes from somewhere else. decodeRaw and encodeRaw leave the JSON as text, so a file read and written that way is byte for byte the same, which encode cannot promise: JSON.stringify writes a float Max saved as 1.0 as 1, which Max reads back without complaint, and the indentation and line endings are whatever the indent and newline options say (4 spaces and \n by default, which is how Max 9.1 writes them on a Mac).

The format

offset  size  content
0       4     "ampf"                        magic
4       4     uint32 LE                     container version, 4
8       4     "aaaa" | "iiii" | "mmmm"      audio effect, instrument, MIDI effect
12      ...   chunks: 4-byte tag, uint32 LE length, payload

Two chunks are known and both are always present. meta holds one uint32, whose meaning is not documented: it is 0 or 1 and does not follow the device type, the Max version, the dependencies or anything else looked at, so the library reads it into meta and writes it back. ptch holds the patcher JSON as UTF-8 with a trailing NUL that the length counts. A device's external dependencies are not appended as further chunks; they are listed in the JSON under dependency_cache, and embedded bpatchers carry their whole subpatcher inline, which is why a device can be a megabyte of text. Any chunk with another tag is kept in chunks and written back in place.

Install

npm install @tsln/amxd

It is a plain Node package, usable from require or import, with no dependencies. Node 20 or later.