Twist Commando // Developer Notes TC-17Distribution: unlimited · No charge
Twist Commando Download

For developers

Make your app turnable.

Twist Commando puts knobs, faders and pads (a Midi Fighter Twister, a Komplete Kontrol, a web page) on the things people use. Here's how your Mac app can be one of them: best through Applug, where your controls appear by name; or from a MIDI mapping that takes minutes.

Choosing a way in

A Twist Commando control is set to control something: a "target". Your app can be reached through these:

Way inYour app needsTurnsShows your stateNames
ApplugThe Applug package (or its JSON lines)Levels and stepsValue, its text and on/off, liveYes: yours, by name
MIDI portMIDI input (MIDI Learn)Absolute or relativeValue and on/off, liveNo: the user names the control
Mackie ControlMackie Control host supportFaders, pots, buttonsValues, lights, meters, liveYes: from your display
ShortcutsApp Intents0–100, coalescedValue and on/off, polledNo
Command lineA CLI or URL scheme0–100, coalescedValue and on/off, polledNo
Keyboard shortcutsMenu shortcutsOne key per stepNoNo

Building something new? Use Applug: a few dozen lines, and your controls appear in Twist Commando by name, with your value text on the screens and in the on-screen indicator. If your app already has MIDI Learn, it already works: point your users at the MIDI port, and send your values back on the same numbers so the controller's lights follow.

The Twist Commando MIDI port

Twist Commando makes a virtual MIDI device named Twist Commando, with a source (what Twist Commando sends) and a destination (what your app sends back). It appears in Core MIDI like any device and keeps the same unique IDs across launches, so your saved mappings stay valid. A control set to MIDI Out sends on it.

What a control sends

  • Turns, absolute: Control Change, 0–127, as the value moves. The default.
  • Turns, relative: Control Change, one message per step: 1 for up, 127 for down (two's complement). For your app's own endless values, such as a playhead.
  • Presses: a note (note on velocity 127, then note off), or a Control Change (127, then 0). With Toggle on, each press alternates on and off instead of sending press and release.

The user can choose any channel and number. Unless they do, a control's numbers come from where it is, so a whole controller can be learned without typing anything:

ChannelBank 1–4 → channels 1–4; on the Shift layer, 5–8
Turn CC20 + the control's position in the bank (20–35 for the Twister's 16 knobs)
Press note36 + its position (36–51), or as a CC, 52 + its position (52–67)

What your app sends back

Send to the Twist Commando destination, on the same channel and number:

  • A value: the turn's CC with your current value (0–127). The knob's ring or fader light moves to it, and the window and web pages show it. Send it whenever the value changes in your app (from the mouse, automation, undo), and once when your app starts, so the controller agrees with you.
  • On or off: the press's note (note on with velocity above 0 for on; note off or velocity 0 for off), or its CC (above 0 for on, 0 for off). The control's switch light follows: a lit mute, a playing deck.

Absolute turns pick up from your value: after you send 64, a turn continues from 64, not from wherever the knob was. That's what makes two-way feel right.

Example

// Your app's "Volume" was learned as CC 20, channel 1.
// Twist Commando sends:      B0 14 5A        (CC 20 = 90)
// The user moves your slider; send back:
//                             B0 14 40        (CC 20 = 64: the ring moves)
// "Mute" was learned as note 36; mute in your app, send:
//                             90 24 7F        (note on: the switch lights)

Mackie Control

For the richest link (track names on the Komplete Kontrol's and SL MkIII's screens, meters on the rings, every button lit), Twist Commando is a Mackie Control Universal surface with up to three extenders, on virtual ports named Twist Commando MCU and Twist Commando MCU XT 1–3. If your app can drive a Mackie Control (as DAWs do), users add it as a surface on those ports and assign controls to its strips.

  • Display: SysEx F0 00 00 66 14 12 <offset> <ASCII…> F7, two lines of 56 characters, 7 per strip: names on top, values below.
  • Faders: pitch bend on channels 1–8 (9 is the master), 14 bits, both ways. Moves come with touch, note 0x68 + strip.
  • V-Pots: turns arrive as CC 0x10 + strip (bit 6 set is anticlockwise, the low bits the speed); set the ring with CC 0x30 + strip.
  • Buttons and lights: notes Rec 0x00, Solo 0x08, Mute 0x10, Select 0x18 (+ strip), V-Pot press 0x20; banks 0x2E/0x2F, channels 0x30/0x31. Light with note on 0x7F (0x01 blinks).
  • Meters: channel pressure strip << 4 | level (0–12). Send them about every 100 ms while playing; they fall on their own.
  • Handshake: send the device query (SysEx 00); Twist Commando answers with its serial and challenge (01); reply with 02; it confirms with 03.

Its controls are under Logic Pro in Twist Commando's menus, where it was built and tested. Any app that drives a Mackie Control the way Logic does should work too, but test yours.

Shortcuts and App Intents

An Apple Shortcut control runs shortcuts: one when pressed, one as it turns, and one now and then to read your state back. Expose what your app does as App Intents and users build those shortcuts in a minute.

Each run gets JSON as its input:

{"event": "turn", "value": 62, "previous": 59, "delta": 3, "on": true, "bank": 1, "knob": 5}
  • event is push, turn or status; value and previous are 0–100.
  • Turns are coalesced: one run at a time, then the latest value. Keep turn intents quick.
  • Reporting back: return a number 0–100 (moves the ring), on/off or a boolean (sets the switch light), or a dictionary like {"value": 45, "on": true} for both. A status shortcut does the same every few seconds, so changes made in your app show.

Good intents for this: set a value (with a 0–100 parameter), get a value, toggle, and get on/off.

Command-line tools

A Run Command control runs a shell command (in the user's shell) when pressed or turned, and can run one for status. If your app has a command-line tool or a URL scheme (open "yourapp://set?volume=$TWIST_VALUE"), that's enough. The same details arrive as environment variables:

TWIST_EVENT=turn  TWIST_VALUE=62  TWIST_PREVIOUS=59  TWIST_DELTA=3
TWIST_ON=1  TWIST_BANK=1  TWIST_KNOB=5

Print what you'd return from a shortcut (45, on, or {"value": 45, "on": true}) to move the ring and light.

Keyboard shortcuts

A Keyboard Shortcut control sends one shortcut per step each way as it turns (say ⌘[ and ⌘]), and another when pressed. It needs nothing from your app but menu shortcuts, and shows nothing back, so it's the last resort.

The other way: the web API

To read or drive Twist Commando itself (show its controls in your app, or press one from it), use the local HTTP server behind its web pages. The user turns it on in Settings › General › Web Pages. It listens on http://127.0.0.1:9033; from another device it needs the code shown there, as ?code=123456 once (a cookie remembers it). Requests from other websites' pages are refused.

Watching: GET /events?device=<name>

Server-sent events. Without device, the controller shown in its window. Each data: line is JSON:

  • "type": "bank", on every change and a few times a second: device, deviceName, bank, bankName, banks, bankNames, shifted, meterView, connected, setups and setup, scenes and scene (ids and names), and controls: each with slot, control ("Knob 3"), kind, assigned, name, value (0–1), text ("−6.2 dB"), on, hue (0–1), meter, pressable and turnable.
  • "type": "activity" when a control is used: what the on-screen indicator shows (title, subtitle, value, hue).
  • Add levels=1 for meters on every control, about 20 times a second.

Acting: GET /act?device=<name>&cmd=…&t=<token>

Every action carries the token from GET /token as t. It changes each time Twist Commando starts: on a 409 reply, fetch it again.

pressslot, on=1 then on=0: presses and releases a control
turnslot, value 0–1: moves a control to a value
bankbank 0–3
shift, meterToggle Shift, or the meter view
setup, sceneid: switch to a setup, restore a scene
rename, colorslot with name, or color (a value from /control, or empty for none)

Use each control's slot from /events as it is. GET /control?device=…&slot=…&t=… describes one control, with the colors to choose from.

Making it feel right

  • Always send your state back. A ring that shows your real value, and a light that shows your real mute, is what makes a controller trustworthy. Send on change, not on a timer.
  • Absolute for things with ends (volumes, mixes), relative for things without (scrolling, nudging, a playhead). Treat each relative step as one fine adjustment: a fast turn sends more of them.
  • Keep MIDI mappings stable. Save them by the port's name or unique ID, and never assume a channel layout beyond what the user learned.
  • Don't echo. When a value arrives from Twist Commando, don't send the same value straight back (it's harmless, but wasteful).

Building something with Twist Commando? We'd like to hear about it, and to list it.