Skip to content

The .swproj format ​

A Stagewright project file is plain JSON on your disk. Sync it with git, Dropbox, iCloud, or a USB stick, the file is the canonical project, no sidecar database, no cloud lockin.

Schema ​

json
{
  "schemaVersion": 3,
  "activeRackId": "rack-1",
  "script": "// TypeScript source\n…",
  "audio": {
    "inputDeviceName": "Aggregate Device",
    "outputDeviceName": "Aggregate Device",
    "sampleRate": 192000,
    "bufferSize": 192
  },
  "midi": {
    "inputs": [
      { "nodeId": "midi-in",   "deviceName": "MIDI Mix" },
      { "nodeId": "midi-in-2", "deviceName": "MPK mini" }
    ]
  },
  "racks": [
    {
      "id": "rack-1",
      "name": "Live Rig",
      "plugins": [...],
      "meters": [...],
      "connections": [...],
      "positions": {...},        // UI-only: card x/y
      "performer": {...},        // UI-only: widget layout
      "canvasExtras": {          // UI-only: mixer strip labels, etc.
        "mixerStripLabels": {
          "plugin-2": ["Piano", "Guitar L", "Guitar R", "Drums"]
        }
      }
    }
  ],
  "bypassGates": [...]            // Boolean-control → plugin bypass wires
}

Schema versioning ​

schemaVersion: 3 is the current. The engine loader handles older versions transparently:

  • v1: flat plugins + connections, no rack concept. Migrated to a single default rack.
  • v2: introduced racks but no canvasExtras. Loads cleanly; new cosmetic state defaults to empty.
  • v3 (current): adds canvasExtras round-trip.

A loaded project is always re-saved at the current version when the user hits Cmd-S. Editing the JSON in a text editor and reopening it is safe, Stagewright validates the schema and warns about unknown fields.

What's in each rack ​

  • plugins[], list of { nodeId, identifier, bypassed, state }. identifier is the engine's stable plugin descriptor id ("AudioUnit-Tuner-v1.0" etc.); state is the plugin's own serialised state (base64-encoded). Renames live in positions[nodeId].name (UI-only) so the engine identifier stays stable.
  • meters[], vumeter / true-peak / utility nodes that aren't full plugins. Same shape as plugins, narrower.
  • connections[], { fromNode, fromPort, toNode, toPort } wires.
  • positions, { nodeId: { x, y } }. Canvas layout.
  • performer, the full stage layout (strips, widgets, bindings, groups). Opaque to the engine, round-trips verbatim.
  • canvasExtras, wiring-canvas cosmetics that don't fit elsewhere (mixer strip labels, more to come). Same opaque round-trip pattern as performer.

Bypass gates ​

bypassGates[] is project-scoped, not per-rack, because they wire arbitrary plugin pairs (a BooleanControl driving N plugins' bypass state). Each entry is { targetNodeId, sourceNodeId }.

Logical control names, hardware abstraction ​

The MIDI input cards' deviceName is the logical name; the engine resolves it to the matching macOS / Windows MIDI device at load time. If a device isn't connected, the card stays in the graph with a warning, re-plug the controller, click "Refresh devices" in the inspector, and the binding restores itself.

Editing externally ​

.swproj is plain JSON, open it in any editor, your IDE, git diff tools, whatever. Stagewright re-saves on Cmd-S so any external edits round-trip cleanly. The script field is a raw TypeScript string; some people prefer to edit the script in their external editor and re-paste, others prefer Monaco inside Stagewright, both work.