Affinity Automation: How to Script and Automate the Entire Affinity Suite on macOS
Affinity Automation: How to Script and Automate the Entire Affinity Suite
Affinity Designer, Affinity Photo, and Affinity Publisher are serious creative tools. They handle vector illustration, photo editing, and page layout at a professional level. What they don't handle well is automation. None of the three Affinity apps ship with a public scripting API, a plugin SDK, or a command line interface. If you need to batch process files, chain operations across apps, or build repeatable workflows, you need to work around these limitations.
This guide covers every viable approach to automating the Affinity suite on macOS, from the built-in tools to external automation frameworks and AI-powered desktop agents.
The Automation Gap: Affinity vs. Adobe
Before choosing an approach, it helps to understand exactly what you're working with and what you're missing.
| Capability | Adobe (Photoshop, Illustrator, InDesign) | Affinity (Photo, Designer, Publisher) | |---|---|---| | Built-in scripting engine | ExtendScript, UXP, CEP panels | None | | Plugin/extension SDK | Full SDK with marketplace | None | | Command line interface | Yes (headless rendering, batch ops) | None | | Macro recording | Actions panel (Photoshop) | Macro panel (Photo, Designer) | | Inter-app scripting | COM/AppleScript bridge | Not supported | | Headless/server mode | Yes (Generator, Node.js) | Not available |
The gap is real. Adobe gives you five or six different ways to automate. Affinity gives you one (macros) and it only works inside the app's GUI. Everything else requires external tools.
Built-in Macros: The Starting Point
All three Affinity apps (Designer 2, Photo 2, Publisher 2) include a macro recorder. Open it through View > Studio > Macro, click Record, perform your operations, then stop and save.
What macros handle well
- Applying consistent adjustments (levels, curves, color balance) across images in Affinity Photo
- Repeating vector operations (stroke changes, fill swaps, alignment) in Affinity Designer
- Text formatting sequences in Affinity Publisher
Where macros fall short
- No file I/O: macros can't open files, save to specific paths, or iterate over a folder
- No conditionals: you can't branch based on image dimensions, color profile, or filename
- No cross-app chaining: a macro in Photo can't hand off to Designer
- No variables: every value is hard-coded at record time
- No CLI trigger: you must click Play inside the GUI
Macros are useful for repetitive in-app tasks, but they cannot drive a real automation pipeline on their own.
AppleScript and System Events
Since Affinity apps don't expose a scripting dictionary, you can't send direct AppleScript commands like you would with Photoshop or Keynote. You can, however, use System Events to drive menu items and simulate keyboard shortcuts.
-- Open a file in Affinity Photo via System Events
tell application "Affinity Photo 2" to activate
delay 1
tell application "System Events"
tell process "Affinity Photo 2"
-- File > Open
click menu item "Open…" of menu "File" of menu bar 1
delay 0.5
-- Type the file path in the dialog
keystroke "g" using {command down, shift down}
delay 0.3
keystroke "/Users/you/photos/input.jpg"
keystroke return
delay 1
keystroke return
end tell
end tell
This works but it is fragile. Menu names change between versions, timing depends on system load, and dialogs can appear unexpectedly. You also need to grant accessibility permissions in System Preferences.
For batch operations, wrap the AppleScript in a shell loop:
#!/bin/bash
for file in ~/photos/batch/*.jpg; do
osascript -e "
tell application \"Affinity Photo 2\" to activate
delay 0.5
tell application \"System Events\"
tell process \"Affinity Photo 2\"
click menu item \"Open…\" of menu \"File\" of menu bar 1
delay 0.5
keystroke \"g\" using {command down, shift down}
delay 0.3
keystroke \"$file\"
keystroke return
delay 2
-- Run your macro here via the Macro panel
keystroke return
end tell
end tell
"
done
Warning
AppleScript automation through System Events is timing-dependent. A slow disk, large file, or unexpected dialog will break the script. Always add error handling and generous delays for production batch jobs.
macOS Accessibility API (AXUIElement)
For more reliable programmatic control, you can use the macOS Accessibility API directly. This gives you structured access to UI elements rather than relying on menu names and keyboard shortcuts.
import ApplicationServices
// Get the Affinity Photo process
let apps = NSWorkspace.shared.runningApplications.filter {
$0.bundleIdentifier == "com.seriflabs.affinityphoto2"
}
guard let app = apps.first else { exit(1) }
let element = AXUIElementCreateApplication(app.processIdentifier)
// Find and click the Export button
var children: CFTypeRef?
AXUIElementCopyAttributeValue(element, kAXChildrenAttribute as CFString, &children)
// Walk the element tree to find your target...
The Accessibility API approach gives you:
- Element discovery by role, title, or identifier
- Reliable button clicks and text entry without timing hacks
- Ability to read UI state (is a dialog open? what's the current tool?)
- Works with all three Affinity apps using the same API
The downside is complexity. You're writing compiled Swift code to navigate a UI tree that Serif didn't design for external consumption. Element hierarchies change between updates, and some controls use custom drawing that doesn't expose accessibility labels.
Shell Scripts for File-Level Automation
Many "Affinity automation" tasks are really file management tasks. You don't need to script the app itself if you can handle the input/output pipeline externally.
#!/bin/bash
# Batch convert Affinity files to PDF using the macOS print system
INPUT_DIR="$HOME/projects/branding"
OUTPUT_DIR="$HOME/exports/pdf"
mkdir -p "$OUTPUT_DIR"
for file in "$INPUT_DIR"/*.afdesign; do
name=$(basename "$file" .afdesign)
# Open in Affinity Designer, export via print dialog
open -a "Affinity Designer 2" "$file"
sleep 3
osascript -e '
tell application "System Events"
tell process "Affinity Designer 2"
keystroke "p" using {command down}
delay 1
click menu button "PDF" of sheet 1 of window 1
delay 0.5
click menu item "Save as PDF" of menu 1 of menu button "PDF" of sheet 1 of window 1
end tell
end tell
'
sleep 2
done
For image processing that doesn't require Affinity-specific features, consider using ImageMagick or sips (macOS built-in) alongside your Affinity workflow:
| Tool | Best for | Speed |
|---|---|---|
| sips (built-in) | Resize, convert, rotate, color profile | ~50ms per image |
| ImageMagick | Complex transforms, compositing, batch watermarks | ~200ms per image |
| Affinity Photo macros | Affinity-specific filters, RAW processing, persona edits | ~2-5s per image (GUI) |
| AI Desktop Agent | Any Affinity operation, including multi-step workflows | ~3-10s per operation |
AI Desktop Agents: The Modern Approach
The most flexible way to automate Affinity apps is with an AI desktop agent. These agents use computer vision to see the screen and accessibility APIs to interact with UI elements. They can handle any workflow a human can perform, including complex multi-step operations that cross app boundaries.
The key advantage: you describe what you want in natural language, and the agent figures out the clicks, menus, and dialogs. No fragile scripts, no hard-coded coordinates, no timing hacks.
# Conceptual example: AI agent automating Affinity Photo
agent.run("""
Open each .jpg in ~/photos/raw/
In Affinity Photo:
1. Apply the 'Product Shot' macro
2. Export as PNG at 2x resolution to ~/photos/export/
3. Close the file without saving the original
""")
What makes AI agents different from traditional automation:
Cross-App Workflows: Chaining Designer, Photo, and Publisher
One of the most common requests is automating workflows that span multiple Affinity apps. For example: process photos in Photo, place them in a Designer template, then assemble everything in Publisher for print output.
Here's a practical pipeline structure:
#!/bin/bash
# Three-app Affinity pipeline
PHOTOS_DIR="$HOME/project/photos"
TEMPLATES_DIR="$HOME/project/templates"
OUTPUT_DIR="$HOME/project/output"
# Step 1: Batch process photos in Affinity Photo
for photo in "$PHOTOS_DIR"/*.raw; do
open -a "Affinity Photo 2" "$photo"
sleep 3
# Trigger macro via keyboard shortcut (assign one in Preferences)
osascript -e 'tell app "System Events" to keystroke "1" using {control down, option down}'
sleep 5
# Export
osascript -e 'tell app "System Events" to keystroke "e" using {command down, option down, shift down}'
sleep 2
done
# Step 2: Open template in Affinity Designer, place processed photos
open -a "Affinity Designer 2" "$TEMPLATES_DIR/layout.afdesign"
sleep 3
# ... place images via automation
# Step 3: Export final output from Publisher
open -a "Affinity Publisher 2" "$TEMPLATES_DIR/book.afpub"
sleep 3
# ... export PDF
Tip
Assign keyboard shortcuts to your most-used macros in each Affinity app (Preferences > Keyboard Shortcuts > Macros). This makes them triggerable from AppleScript or System Events without navigating the macro panel.
Common Pitfalls
- Timing assumptions: The biggest source of failures. Affinity apps load files at different speeds depending on file size, complexity, and whether the app was already running. Always check for UI state changes rather than using fixed
sleepvalues when possible. - Version-specific menu paths: Menu item names and positions change between Affinity v1 and v2. Scripts targeting "Affinity Photo" won't find "Affinity Photo 2" and vice versa. Always use the exact bundle identifier or app name for your installed version.
- Accessibility permissions not granted: macOS blocks System Events and Accessibility API access by default. You need to add your script runner (Terminal, iTerm, your automation tool) to System Settings > Privacy & Security > Accessibility.
- Modal dialogs blocking the pipeline: If Affinity shows an unexpected dialog (crash recovery, update notification, color profile mismatch), your automation stalls. Build in dialog detection and dismissal logic.
- Trying to run headless: None of these approaches work without a display. Affinity apps require a GUI session. For server-side batch processing, use a different tool (ImageMagick, Inkscape CLI, or a virtual display with VNC).
Choosing Your Approach: Decision Checklist
Use this to pick the right automation method for your specific situation:
- Is the task a single repeated operation inside one Affinity app? Use built-in macros. Record once, assign a keyboard shortcut, done.
- Do you need to batch process files through an Affinity macro? Combine shell scripts (for file iteration) with AppleScript (to open files and trigger the macro).
- Do you need cross-app workflows or complex multi-step operations? Use an AI desktop agent. The flexibility is worth the per-operation speed cost.
- Can the task be done without Affinity-specific features? Skip Affinity entirely. Use
sips, ImageMagick, Inkscape CLI, or Ghostscript. These run headless and are orders of magnitude faster. - Do you need a production pipeline that runs unattended? Consider whether Affinity is the right tool. For high-volume automated processing, tools with proper CLI and API support (Photoshop, GIMP with Script-Fu, Inkscape) are more reliable.
Wrapping Up
Affinity's automation story is limited by design. There's no scripting API on the horizon, and Serif has been clear that their focus is on the creative experience rather than developer tooling. That said, macOS provides enough hooks (accessibility APIs, System Events, shell scripting) to build real automation around these apps. For the most flexible approach, AI desktop agents combine all of these techniques and add visual understanding on top.
For deeper dives into specific apps, see our guides on Affinity Designer automation and Affinity Photo automation.
Fazm is an open source macOS AI agent that can automate any desktop application, including the full Affinity suite. Open source on GitHub.