A Stream Deck plugin that lets you build MIDI presets for the Moog 500 Series Analog Delay and commit them to QLab as ready-to-fire cue groups.
┌──────────────────────────────────────────────────────────┐
│ STREAM DECK │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Delay │ │Feedback │ │LFO Shape│ │ COMMIT │ │
│ │ 500ms │ │ 45% │ │ Sine │ │ 4 params│ │
│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │ │
│ Stream Deck + encoder devices: │
│ Dial rotate = adjust value │
│ Dial press = reset to default │
│ Touch tap = open input dialog │
└───────┼────────────┼───────────┼────────────┼────────────┘
│ │ │ │
▼ ▼ ▼ │
┌─────────────────────────────────────┐ │
│ macOS `defaults` persistent store │ │
│ Domain: com.moog500.presetbuilder │ │
│ │ │
│ param.Feedback = {value:45, ...} │ │
│ param.LFO Shape = {value:"Sine"} │ │
│ param.Delay Time = {value:500} │ │
│ global.qlabHost = "127.0.0.1" │ │
│ global.midiChannel = 1 │ │
└───────────────────┬─────────────────┘ │
│ │
│ ◄──── reads ───────┘
▼
┌─────────────────────────────────────┐
│ Parameter Resolver │
│ Converts user values → MIDI CCs │
│ │
│ "Feedback 45%" → CC13=57, CC45=0 │
│ "500ms" → CC76=0, CC74=64, │
│ CC75=0, CC12=75, CC44=50│
└───────────────────┬─────────────────┘
│
▼ OSC over UDP
┌─────────────────────────────────────┐
│ QLab (port 53000) │
│ │
│ Created cue group: │
│ ┌─ "Moog 500 Preset" (Timeline) ─┐│
│ │ CC76=0 Sync OFF pre: 0.0s ││
│ │ CC74=64 Range 1.0x pre: 0.1s ││
│ │ CC75=0 Mult 1x pre: 0.2s ││
│ │ CC12=75 Time MSB pre: 0.3s ││
│ │ CC44=50 Time LSB pre: 0.4s ││
│ │ CC13=57 Fdbk MSB pre: 0.5s ││
│ │ CC17=16 LFO Sine pre: 0.6s ││
│ └─────────────────────────────────┘│
└─────────────────────────────────────┘
- macOS 10.15 or later
- Stream Deck software 6.6 or later
- Node.js 20 or later (for building; Stream Deck bundles its own runtime)
- QLab 4 or 5 (with OSC access enabled on port 53000)
- Stream Deck CLI:
npm install -g @elgato/cli
Place this folder wherever you keep projects.
cd /path/to/this/project
npm installnpm run buildThis bundles src/ into com.moog500.presetbuilder.sdPlugin/bin/plugin.js.
streamdeck link com.moog500.presetbuilder.sdPluginThe plugin should now appear under the Moog 500 Delay category in the Stream Deck action list.
npm run watchThis rebuilds on file changes and auto-restarts the plugin.
Stage 1: Real-Time MIDI Control
- Each parameter button has its Set Parameter action assigned
- When you press a button (or rotate an encoder), the plugin immediately:
- Prompts you for a value (dialog, dropdown, or dial input)
- Sends MIDI CC messages directly to your configured MIDI device
- Stores the value persistently
- Result: You hear/see the change on the Moog 500 in real-time ✓
Stage 2: QLab Cue Creation
- Once you've dialed in a sound you like, press the Commit to QLab button
- The plugin reads all stored parameter values and creates a QLab cue group
- You'll be prompted for a meaningful cue name (with a default sequential suggestion like "Preset 001")
- Result: A new QLab cue ready to fire with this exact preset ✓
1. Press "Delay Time" button → enter 500ms → MIDI sent to Moog, stored
2. Press "Feedback" button → enter 45% → MIDI sent to Moog, stored
3. Press "LFO Shape" button → select Sine → MIDI sent to Moog, stored
(You hear all these changes in real-time on the Moog 500)
4. Press "COMMIT" button → name it "Synth Pad" → QLab cue created
5. Later, press the "COMMIT" button again → name it "Delay Effect" → another QLab cue
Each preset is independent in QLab. You can build multiple presets in a single session.
The Commit button's Property Inspector includes a Parameter Filters checklist. Each of the 24 parameters has a checkbox — all enabled by default.
When a parameter is deselected:
- It is excluded from the QLab cue group when you press Commit
- Its parameter button is silenced — adjusting it stores the value but does not send MIDI CCs to the hardware
This lets you scope a commit to only the parameters you care about for a given preset (for example, committing only delay time and feedback while ignoring LFO settings), or mute specific parameters during live adjustment without removing their buttons from the deck.
Use Select All / Deselect All at the top of the list to quickly reset the filter.
- In the Stream Deck app, find Moog 500 Delay in the action list
- Drag "Set Parameter" onto a button (or encoder dial on Stream Deck +)
- In the Property Inspector (right panel), select the parameter from the dropdown
- The button is now ready
On a standard button (Keypad):
- Press the button → a native macOS dialog appears
- For continuous parameters: enter a number (e.g.,
45for 45% feedback) - For discrete parameters: pick from a list (e.g., "Sine" for LFO Shape)
- For BPM Sync delay: you'll be prompted for BPM, beat unit, then beat division
On a Stream Deck + encoder (dial):
- Rotate the dial to adjust the value (fine adjustment when pressed)
- Press the dial to reset to the parameter's default value
- Touch the display strip to open the input dialog
The button displays the current stored value at all times.
✓ MIDI CC messages are sent immediately to your configured MIDI device as you adjust each parameter. This lets you hear changes in real-time on the Moog 500.
- Drag the "Commit to QLab" action onto a button
- Configure QLab connection and MIDI settings in the Property Inspector:
- MIDI Output Device: select your MIDI interface (configure once, used for all parameter buttons)
- MIDI Channel: match the Moog 500's MIDI channel (default 1)
- QLab Host:
127.0.0.1for local, or the IP of a remote Mac - OSC Port:
53000(default) - MIDI Patch in QLab: which QLab MIDI patch routes to your Moog 500 (1–8)
- Pre-Wait Interval: time between each CC message (default 0.1s)
- Target Cue List: (optional) if set, created cues go here
- Parameter Filters: checkboxes for each parameter — deselected parameters are excluded from the QLab commit and suppressed from real-time MIDI output
- Press the Commit button
- You'll be prompted to name the preset cue with a default sequential name (e.g., "Preset 001")
- Edit or confirm the name
- The plugin creates a Timeline Group cue in QLab containing one MIDI cue per CC, each with a cumulative pre-wait
Note: The Commit button only sends to QLab and creates cues. MIDI was already sent to your device in real-time as you adjusted each parameter.
Ensure QLab is configured to accept OSC:
- Open Workspace Settings → Network → OSC Access
- Verify port 53000 is active (or set your custom port)
- If using a passcode, note that this plugin doesn't currently support passcode auth
- Ensure your MIDI patch is routed to the interface connected to the Moog 500
| Parameter | Description | Output CCs |
|---|---|---|
| Delay Time (BPM Sync) | Musical delay from BPM + beat division | CC76, CC74, CC75, CC12, CC44 |
| Delay Time (Manual) | Direct ms entry (35–6400) | CC76, CC74, CC75, CC12, CC44 |
| Parameter | CC MSB | CC LSB | Resolution |
|---|---|---|---|
| Feedback | 13 | 45 | 7-bit (MSB only) |
| LFO Rate | 15 | 47 | 7-bit (MSB only) |
| LFO Amount | 16 | 48 | 7-bit (MSB only) |
| LFO Duty Cycle | 20 | 52 | 14-bit |
| Time Slew Rate | 5 | 37 | 14-bit |
| Parameter | CC | Options |
|---|---|---|
| LFO Shape | 17 | Off, Sine, Triangle, Square, Saw, Ramp, S&H, Smooth S&H |
| Filter Mode | 89 | Bright, Dark |
| Time Range | 74 | Short (0.5x), Long (1.0x) |
| Time Multiplier | 75 | 1x, 2x, 4x, 8x |
| Time Sync | 76 | Off, On |
| Time Clock Div | 77 | 4 Whole through 1/32 Triplet |
| LFO Sync | 78 | Off, On |
| LFO Clock Div | 79 | 4 Whole through 1/32 Triplet |
| Pitch Bend Amt | 80 | Off, 2–24 semitones |
| MIDI Note Mode | 82 | Off, Delay Time |
| Mod Wheel→LFO | 85 | Off, On |
| Tap Multiplier | 86 | 1x, 2x, 3x, 4x |
| Tap/Sync Dest | 87 | Time, LFO |
| CV Input Mode | 90 | Tap, Time, Feedback, LFO Rate, LFO Amt, LFO Shape |
| Tap Polarity | 114 | Normally Closed, Normally Open |
| Time LED Div | 116 | x1 through x8 |
| LFO Note Reset | 73 | Off, On |
All values are stored in macOS defaults under the domain com.moog500.presetbuilder.
You can inspect stored values from Terminal:
# View all stored data
defaults read com.moog500.presetbuilder
# View a specific parameter
defaults read com.moog500.presetbuilder "param.Feedback"
# Clear everything
defaults delete com.moog500.presetbuilder.
├── com.moog500.presetbuilder.sdPlugin/ # Plugin bundle (deployed to Stream Deck)
│ ├── manifest.json # Plugin metadata & action definitions
│ ├── bin/
│ │ └── plugin.js # Bundled plugin code (built by rollup)
│ ├── layouts/
│ │ └── parameter-layout.json # Encoder touch-strip layout
│ ├── static/imgs/ # Icons
│ │ ├── plugin/
│ │ ├── actions/parameter/
│ │ └── actions/commit/
│ └── ui/
│ ├── set-parameter.html # Property Inspector for parameter action
│ └── commit.html # Property Inspector for commit action
├── src/ # Source code
│ ├── plugin.js # Entry point
│ ├── parameters.js # All parameter definitions & calculations
│ ├── defaults-store.js # macOS `defaults` persistence layer
│ ├── qlab-osc.js # OSC client for QLab communication
│ └── actions/
│ ├── set-parameter.js # Set Parameter action handler
│ └── commit.js # Commit to QLab action handler
├── package.json
├── rollup.config.mjs
└── README.md
The delay time computation mirrors the spreadsheet logic:
- Target ms =
(60000 / BPM) × beatUnitMultiplier × beatDivisionMultiplier - Auto-select multiplier: ≤800ms→1x, ≤1600ms→2x, ≤3200ms→4x, else→8x
- Base ms = targetMs / multiplier
- Range mode: base ≤400ms → Short (0.5x), else → Long (1.0x)
- 14-bit normalization:
- Short:
(base - 35) / (400 - 35)→ 0–16383 - Long:
(base - 70) / (800 - 70)→ 0–16383
- Short:
- Split: MSB = floor(value / 128), LSB = value % 128
Maximum achievable delay: 800ms × 8x = 6400ms.