Adapting SoundFlow Scripts to Use SFX
Adapting SF scripts to SFX
SFX (The SoundFlow Extension Framework) is a transformative new technology layer built for the future of SoundFlow integrations.
Legacy scripts written using macOS accessibility to automate with SoundFlow will continue to work, and users can opt in to the faster, more stable SFX architecture with one line of code.
See the list of supported software which can take advantage of SFX at the end of this article.
SFX expands what's possible inside of SFX enabled apps. Scripts and macros can now:
- Bypass the need to activate or focus a SFX app's main window.
- Bypass Open and Save dialogs.
- Select items from pop-up menus (with or without modifiers) without opening them.
- Access previously unavailable UI elements within SFX enabled application interfaces.
- Address UI elements by their unique, persisted ID names.
- Trigger DAW commands directly.
What should work?
SFX is opt-in, so generally, your scripts should continue to work when transitioning to SFX. There are, however, times where you'll need to update your scripts to take full advantage of SFX. In the best case, it's a single line of code, in other cases, they'll need a bit more work.
Once migrated, your scripts will be more solid, reliable, faster, and in many cases be able to work more in the background, without the need for keyboard and mouse simulation or the system accessibility layer.
If you run into any issues transitioning your existing scripts, reach out via the Help workflows.
To Get Started
To start the process of transitioning a script to SFX, all you need to do is add the following line of code at the top of your script, which defaults SFX to be "On".
sf.ui.useSfx();
Temporarily disable SFX
There may be times when you need to revert certain parts of your scripts to use traditional UI automation instead of SFX. To temporarily disable SFX for a specific section of code, you can wrap it in a try/finally block like this:
// Disable SFX
sf.ui.useSfx({ value: "Off" });
try {
// Add your non-SFX code here...
} finally {
// Re-enable SFX
sf.ui.useSfx();
}
By structuring the code this way, SFX will automatically be re-enabled once the block completes, even if an error occurs inside the try block.
Do scripts need to be rewritten to use SFX?
In most cases, enabling SFX at the start of your script is all that’s required. However, there may be situations where you need to adjust or update your code to take full advantage of SFX.
Popup Menu Select
Code that uses separate actions for opening a popup menu and choosing elements within the popup menu (for example checkboxes) should transition to using a single popupMenuSelect call.
Here is an example using popupMenuSelect to set the "Audio" checkbox inside the Clip List Filter Plate popup menu.
sf.ui.useSfx();
const clipListFilterPlate = sf.ui.proTools.sfx.mainWindow.buttons.whoseTitle.is("Plate").first;
clipListFilterPlate.popupMenuSelect({
menuPath: ["Audio"],
targetValue: "Enable",
});
Open and Save Dialogs
With SFX, you can now bypass open and save dialogs entirely.
When updating scripts that rely on native file open or save dialogs, you'll need to update the script to pass the new filePaths argument to the .elementClick() call that triggers the dialog (for example, when clicking Save or OK in a window that would normally open a file chooser).
Here’s an example using SFX to perform a “Save Session Copy In…” operation:
sf.ui.useSfx();
sf.ui.proTools.menuClick({
menuPath: ['File', 'Save Session Copy In...'],
});
const okButton = sf.ui.proTools.windows.whoseTitle.is("Save Copy In...").first.buttons.whoseTitle.is("OK").first;
okButton.elementWaitFor();
okButton.elementClick({
filePaths: ['/Users/chr/.../copysave.ptx']
});
Activating Pro Tools Main Window
Optionally, when using SFX you can remove calls like sf.ui.proTools.appActivateMainWindow() & sf.ui.proTools.appActivate() as SFX generally shouldn't need to focus Pro Tools (at all).
You can also replace the calls with the following code that will only activate the main window if the version of Pro Tools the user is using is SFX compatible.
Replace this...
sf.ui.proTools.appActivateMainWindow()
With this...
if (!sf.ui.proTools.isSfxApplication) {
sf.ui.proTools.appActivateMainWindow();
}
Mouse Click Element
Mouse clicking elements programmatically with the mouseClickElement action now works with SFX.
The mouseClickElement() method now also supports native modifier arguments, allowing you to simulate clicks combined with keyboard modifiers such as Command (⌘), Shift, Option (⌥), or Control (⌃), all executed at the framework level without requiring keyboard simulation.
Here is an example that clears all mutes...
if (sf.ui.useSfx && !globalState.OPP_disableSfx) sf.ui.useSfx();
if (!sf.ui.proTools.isRunning) throw `Pro Tools is not running`;
if (!sf.ui.proTools.isSfxApplication)
sf.ui.proTools.appActivateMainWindow();
const muteBtn = sf.ui.proTools.selectedTrack.buttons.getByTitle('Mute');
const wasMuted = muteBtn.value.value === 'on state';
if (!wasMuted) {
muteBtn.elementClick();
for (let i = 0; ; i++) {
sf.wait({ intervalMs: 50 });
if (muteBtn.invalidate().value.value === 'on state') break;
if (i == 9) throw 'Could not mute selected track to allow us to unmute all';
}
}
muteBtn.mouseClickElement({ isOption: true });
If the UI element you're targeting is not accessible to SFX please let us know by reaching out via the Help workflows. In these scenarios please see the section on temporarily disabling SFX.
DAW commands
DAW Commands are discrete Pro Tools actions that SoundFlow exposes and controls through the new SFX layer. Each command corresponds to a specific operation inside Pro Tools and can be triggered programmatically.
DAW commands use SFX rather than relying on macOS Accessibility scripting. This makes them faster, more reliable.
- Each DAW command has a unique persisted name (
uniquePersistedName), allowing scripts to reference it directly. - These commands are accessible through the API via
sf.ui.proTools.sfx.dawCommands.
How do I get a list of all the DAW commands?
You can use the following script to log a full list of Pro Tools DAW commands with their associated unique persisted names.
const dawCommandPersistedNames = sf.ui.proTools.sfx.dawCommands
.reduce((dawCommands, dawCommand) => {
const name = dawCommand.getAttribute("com.avid.ProTools.DawCommand.name");
const uniquePersistedName = dawCommand.uniquePersistedName.replace("CommandID_", "");
if (!dawCommands[name]) {
dawCommands[name] = uniquePersistedName;
}
return dawCommands;
}, {});
log(dawCommandPersistedNames);
Do DAW commands wait for their action to complete before continuing?
Not always. Depending on the command’s functionality, you may need to add additional code to ensure the action has finished before proceeding to the next step. For guidance on specific cases, please start a thread in the "How to" section of the SoundFlow User Forum.
How do I run DAW commands?
DAW Commands can be run in Pro Tools 2025.10+ using the dawCommand's .run() method.
Here are some examples...
// Toggle "Link Edit And Timeline Selection"
const dawCommandName = "LinkEditAndTimelineSelection"
sf.ui.proTools.sfx.dawCommands.getByUniquePersistedName(dawCommandName).run();
// Move Edit Insertion To Next Edit (Tab)
sf.ui.proTools.sfx.dawCommands.getByUniquePersistedName("MoveEditInsertionToNextEdit").run();
// MoveEdit Insertion To Previous Edit (Option + Tab)
sf.ui.proTools.sfx.dawCommands.getByUniquePersistedName("MoveEditInsertionToPreviousEdit").run();
// Extend Edit Insertion To Next Edit (Shift + Tab)
sf.ui.proTools.sfx.dawCommands.getByUniquePersistedName("MoveEditInsertionToNextEdit").run({ isShift: true });
// Extend Edit Insertion To Previous Edit (Option + Shift + Tab)
sf.ui.proTools.sfx.dawCommands.getByUniquePersistedName("MoveEditInsertionToPreviousEdit").run({ isShift: true, });
// Move Edit Selection To Next Region
sf.ui.proTools.sfx.dawCommands.getByUniquePersistedName("MoveEditSelectionToNextRegion").run();
// Move Edit Selection To Previous Region
sf.ui.proTools.sfx.dawCommands.getByUniquePersistedName("MoveEditSelectionToPreviousRegion").run();
Click a DAW command's menu item (Menu Click)
If a DAW Command's Unique Persisted Name is associated with a Pro Tools main menu item. Its associated dawCommand will have a menuItem return property, for extra control.
Here is a simple example of using a DAW Command to replace using an sf.ui.proTools.menuClick.
Before SFX...
sf.ui.proTools.menuClick({
menuPath: ["Options", "Link Timeline and Edit Selection"],
});
After SFX...
const dawCommandName = "LinkEditAndTimelineSelection"
sf.ui.proTools.sfx.dawCommands.getByUniquePersistedName(dawCommandName).menuItem.elementClick();
Enable, Disable and toggle the State of DAW command's Menu Item
You can Enable|Disable|Toggle Menu items with a function like this...
/**
* Sets a DAW menu item state in Pro Tools.
*
* @param {Object} params - The configuration object.
* @param {string} params.dawCommandName - The unique persisted name of the DAW command.
* @param {"Enable" | "Disable" | "Toggle"} params.targetValue - The desired action.
*/
function setMenuState({ dawCommandName, targetValue }) {
const menuItem = sf.ui.proTools.sfx.dawCommands
.getByUniquePersistedName(dawCommandName)
.menuItem;
const isChecked = menuItem.isMenuChecked;
const shouldClick =
(targetValue === "Enable" && !isChecked) ||
(targetValue === "Disable" && isChecked) ||
(targetValue === "Toggle");
if (shouldClick) menuItem.elementClick();
}
// Enable "Link Timeline and Edit Selection"
setMenuState({
dawCommandName: "LinkEditAndTimelineSelection",
targetValue: "Enable"
});
// Disable "Link Timeline and Edit Selection"
setMenuState({
dawCommandName: "LinkEditAndTimelineSelection",
targetValue: "Disable"
});
// Toggle "Link Timeline and Edit Selection"
setMenuState({
dawCommandName: "LinkEditAndTimelineSelection",
targetValue: "Toggle"
});
Entering text into text fields
Before SFX, some Pro Tools text fields required the useMouseKeyboard property to be enabled, forcing keyboard simulation to input text. With SFX, this workaround is no longer needed — omitting or setting useMouseKeyboard to false now allows text to be entered directly, offering a faster and far more reliable approach.
textField.elementSetTextFieldWithAreaValue({
value: "NEW VALUE",
useMouseKeyboard: true // This may not be needed with SFX
});
Toggle SFX Using Global State
If you’d like to toggle SFX behavior on or off globally, you can do so using a global state variable.
Note: Values stored in the globalState object persist only for the current SoundFlow session and will reset when SoundFlow restarts.
// Set Global State to the opposite value
globalState.OPP_disableSfx = !globalState.OPP_disableSfx;
// Log Current Status of Global State
log(!globalState.OPP_disableSfx ? "SFX is Enabled" : "SFX is Disabled");
You can then assign a keyboard trigger to the script to toggle SFX on and off.
For scripts where you’d like to toggle SFX on/off, simply add this line at the beginning of your script:
if (sf.ui.useSfx && !globalState.OPP_disableSfx) sf.ui.useSfx();
Please note if you choose to use the variable OPP_disableSfx, this will also affect commands from SoundFlow's Official Pro Tools package.
SFX Enabled Software
- Pro Tools 2025.10+
Note: Writing macros and scripts for applications not on the list above will use legacy macOS accessibility for automation.