Logic Pro ships with a feature most users walk straight past — Scripter, a JavaScript MIDI plugin that runs sample-accurate inside Logic's audio thread. It is the only major DAW that lets you write real code in the host language of the web. Pair it with a gamepad bridge and you get something genuinely novel: a logic scripter gamepad setup that swaps orchestral articulations from face buttons faster than any keyswitch macro on the market. This guide walks the whole stack — bridge to Scripter to articulation switching to feedback — with code you can paste.
- What you build: a Logic Pro Scripter script that turns DualSense face buttons into articulation keyswitches for any orchestral library.
- What you need: Logic Pro 10.7+ on macOS 12+, a controller, Universal Controller MIDI.
- Lines of code: ~80. Scripter compiles on save.
- Beats keyswitching with the left hand: articulation flips in 3 ms — your hand never leaves the controller.
Why Scripter is underused
Logic users tend to bounce off Scripter for two reasons. The first is that Apple ships exactly one example and zero documentation links in the plugin itself. The second is that Scripter is invisible until you hit "Open Script in Editor". Once you do, it is a fully featured JavaScript runtime — HandleMIDI, ProcessMIDI, SendMIDIEventNow, and a parameter API that exposes plugin UI. For logic scripter gamepad work specifically, it is the cleanest way to do conditional MIDI logic without leaving Logic. The Logic Pro Scripter manual page is the authoritative reference.
What you'll need
- Logic Pro 10.7 or 11 on macOS 12 Monterey or later.
- Universal Controller MIDI v1.0+ — the bridge.
- Any controller — DualSense, Xbox, Switch Pro, 8BitDo Pro 2.
- An orchestral library with keyswitches (Spitfire BBC SO, Cinematic Studio Strings, Berlin Strings, etc.).
- About 20 minutes for v1, longer if you want every face button on a per-library articulation.
Step-by-step setup
1. Drop Scripter on the track
On your orchestral instrument track, click the MIDI FX slot above the instrument and choose Scripter. The default plugin window pops up with a Parameter section and an "Open Script in Editor" button. The default script is a one-liner that passes MIDI through untouched.
2. Open the editor, paste the articulation switcher
Delete everything in the editor. Paste:
// Logic Pro Scripter — Gamepad Articulation Switcher
// Reads face button notes from Universal Controller MIDI and
// emits a keyswitch on a fixed channel before passing the
// played note through with the chosen articulation.
var NeedsTimingInfo = false;
// Articulation lookup: face button note → keyswitch pitch.
// Default values fit Spitfire BBC SO articulations.
var ARTIC = {
60: 0, // Cross / A → Sustain (C-2)
61: 1, // Square / X → Legato (C#-2)
62: 2, // Triangle / Y → Staccato (D-2)
63: 3 // Circle / B → Pizzicato (D#-2)
};
var currentKS = ARTIC[60];
var kSChannel = 1;
// Plugin parameters exposed to the Logic UI.
var PluginParameters = [
{ name: "Velocity curve", type: "lin", minValue: 0.5, maxValue: 3.0, defaultValue: 1.6, numberOfSteps: 250 },
{ name: "Velocity floor", type: "lin", minValue: 0, maxValue: 80, defaultValue: 18, numberOfSteps: 80 },
{ name: "Keyswitch channel", type: "lin", minValue: 1, maxValue: 16, defaultValue: 1, numberOfSteps: 15 }
];
function HandleMIDI(event) {
// Face button = articulation change. Fire keyswitch, swallow note.
if (event instanceof NoteOn && ARTIC.hasOwnProperty(event.pitch)) {
currentKS = ARTIC[event.pitch];
var ks = new NoteOn();
ks.pitch = currentKS;
ks.velocity = 100;
ks.channel = GetParameter("Keyswitch channel");
ks.sendAfterMilliseconds(0);
var off = new NoteOff();
off.pitch = currentKS;
off.channel = ks.channel;
off.sendAfterMilliseconds(40);
return; // swallow
}
if (event instanceof NoteOff && ARTIC.hasOwnProperty(event.pitch)) {
return; // swallow note-off too
}
// Played notes — apply velocity curve and pass through.
if (event instanceof NoteOn) {
var curve = GetParameter("Velocity curve");
var floor_ = GetParameter("Velocity floor");
var v = event.velocity / 127;
v = Math.pow(v, curve);
event.velocity = Math.max(floor_, Math.floor(v * 127));
}
event.send();
} 3. Run the script
Hit Run Script. The plugin window now shows three sliders — Velocity curve, Velocity floor, and Keyswitch channel. Hit a face button on the controller. Scripter swallows the button's note, fires a keyswitch on the channel you picked, and every subsequent note plays through that articulation. Hit a different face button — articulation flips, instantly.
4. Match the keyswitches to your library
The default pitch values (C-2, C#-2, D-2, D#-2) match Spitfire's BBC SO Discover. Different libraries use different keyswitch pitches. Open the library, hover a keyswitch in Kontakt or the player UI, note the pitch, and edit the ARTIC dictionary at the top of the script:
// For Cinematic Studio Strings:
var ARTIC = {
60: 12, // Cross → Sustain (C-1)
61: 13, // Square → Legato
62: 14, // Triangle → Spiccato
63: 15 // Circle → Tremolo
}; 5. Tune the velocity curve
Orchestral libraries have wide velocity layers. A linear curve from a gamepad trigger does not feel right. Dial the curve to about 1.6 for default behaviour, 2.2 for film score swells (slow build, dramatic top), 0.8 for a flat lyrical playing style.
6. Save the channel strip
Right-click the channel strip header → Save Channel Strip Setting…. Now every new instance of this library loads with Scripter pre-wired. You can have a different Scripter setting per library — keep a folder of them and they all live in Logic's preset menu.
Default mapping
| Face button | Note in | Default articulation | Use |
|---|---|---|---|
| Cross / A | Note 60 | Sustain | Default playing |
| Square / X | Note 61 | Legato | Connected phrases |
| Triangle / Y | Note 62 | Staccato | Short articulations |
| Circle / B | Note 63 | Pizzicato | Plucked passages |
| D-pad up / down | Note 64 / 65 | Tremolo / Trill | Tension, builds |
| L2 / R2 triggers | CC 2 / 11 | Modulation / Expression | Mod wheel, dynamics |
Extending the script — modifier-aware articulations
Want sixteen articulations from four buttons? Use a modifier. Add L1 and R1 as shift keys and rewrite the lookup:
var l1Held = false;
var r1Held = false;
function HandleMIDI(event) {
// Shoulder buttons modify the articulation set.
if (event instanceof NoteOn && event.pitch === 66) { l1Held = true; return; }
if (event instanceof NoteOff && event.pitch === 66) { l1Held = false; return; }
if (event instanceof NoteOn && event.pitch === 67) { r1Held = true; return; }
if (event instanceof NoteOff && event.pitch === 67) { r1Held = false; return; }
if (event instanceof NoteOn && ARTIC.hasOwnProperty(event.pitch)) {
var bank = (l1Held ? 4 : 0) + (r1Held ? 8 : 0);
var ks = ARTIC[event.pitch] + bank;
// Fire keyswitch with the bank offset (see Step 2 script).
}
// etc.
} Suddenly four buttons cover 16 articulations across four banks. That is more articulations than most film scores actually use.
Why this beats Logic's built-in articulation set
Logic does ship Articulation Sets for keyswitches. They are clunky — XML files, no easy hot-swap between libraries, no JavaScript. Scripter is a single text field, hot-reloads on save, and can do anything a Turing machine can. The cost is that you maintain the mapping in code instead of a GUI, but ten lines of ARTIC = { ... } is hardly maintenance overhead.
Gotchas to know
- Scripter swallows MIDI silently on errors. If your script throws, Logic prints to the system console with no in-plugin notification. Always test with Console.app open during development.
- No setTimeout. Use
sendAfterMillisecondson NoteOn / NoteOff events for delayed actions. - State persists across notes. Globals like
currentKSstay set between calls. That is the right design for a stateful articulation switcher. - ES2015-ish. No arrow functions in some Logic versions, no
async/await, nofetch. Plain function expressions only. - Bypass kills the script. If Scripter is bypassed, no MIDI passes through at all. Don't bypass during a take.
Scripter is the most powerful underused tool in Logic. Pair it with a gamepad bridge and your orchestral templates run faster than any $1,200 articulation controller on the market.