Affinity Photo Automation: Scripts, Macros, and AI Agents for Batch Workflows
Affinity Photo Automation: Scripts, Macros, and AI Agents for Batch Workflows
Affinity Photo is fast, affordable, and genuinely good at professional image editing. What it is not good at is automation. Unlike Photoshop, which exposes a full scripting engine (ExtendScript, UXP, CEP panels), Affinity Photo has no public scripting API, no plugin SDK, and no command line interface. If you need to batch process 500 product photos, apply the same edits to a weekly content pipeline, or watermark every export automatically, you are on your own.
This guide covers every viable approach to automating Affinity Photo on macOS, from the built-in macro system to external tools that drive the app through accessibility APIs.
Built-in Macros: What They Can and Cannot Do
Affinity Photo ships a macro recorder that captures a sequence of operations and replays them. You open the Macro panel (View > Studio > Macro), hit record, perform your edits, stop, and save.
Macros handle:
- Filter applications (Gaussian blur, unsharp mask, levels, curves)
- Layer operations (new layer, merge, flatten)
- Resize and canvas operations
- Export (with fixed settings)
- Color adjustments (HSL, vibrance, white balance)
Macros do not handle:
- File open/close (you cannot script "open the next file in a folder")
- Conditional logic (if this pixel is darker than X, do Y)
- Variable parameters (apply blur at a radius that depends on image dimensions)
- UI navigation (switching personas, opening dialogs)
- External triggers (run this macro when a new file appears in a folder)
The macro system is a linear tape recorder. It replays exactly what you did, in order, with the same parameters. This makes it useful for single-image operations you repeat often, but inadequate for true batch automation.
Applying a Macro to Multiple Files
Affinity Photo's "Batch Job" feature (File > New Batch Job) lets you select a folder of images, pick a macro, and apply it to every file. This is the closest thing to built-in batch automation.
File > New Batch Job
→ Add source folder
→ Select macro from library
→ Choose output format (JPEG, PNG, TIFF, PSD)
→ Set output folder
→ Click OK
It works for simple pipelines: resize all images to 1200px wide, apply a sharpening filter, export as JPEG at 85% quality. But the moment you need branching logic, filename-dependent behavior, or integration with other tools, batch jobs hit a wall.
| Feature | Affinity Macros | Photoshop Actions + ExtendScript | AI Desktop Agent | |---|---|---|---| | Record and replay edits | Yes | Yes | Not needed (drives UI directly) | | Batch apply to folder | Yes (Batch Job) | Yes (File > Automate > Batch) | Yes (reads file system, opens each) | | Conditional logic | No | Yes (full JavaScript) | Yes (visual or API-based decisions) | | Variable parameters | No | Yes | Yes | | Cross-app workflows | No | Limited (Bridge integration) | Yes (any app on the desktop) | | File system triggers | No | Folder Actions (macOS) | Yes (watch folder, react) | | Cost | Free (built in) | $22.99/mo (Creative Cloud) | Varies |
macOS Shortcuts and Automator: The Middle Ground
Before reaching for code, macOS itself offers two automation layers that work with Affinity Photo at the file level.
Automator (legacy but still functional on macOS 15) can build a workflow that watches a folder, processes files through a sequence of actions, and moves outputs to a destination. Affinity Photo is not scriptable via AppleScript (it exposes no AppleScript dictionary), so Automator cannot control the app directly. But it can handle the file-level orchestration: rename files, convert formats via sips, resize with ImageMagick, and trigger shell scripts.
Shortcuts (the modern replacement) similarly cannot send commands into Affinity Photo. It can orchestrate file operations around it.
A practical pattern: use Automator or Shortcuts to prepare files (resize, rename, organize into folders), then open them in Affinity Photo's batch processor for the edits that require Affinity's engine (RAW development, frequency separation, lens corrections).
# Pre-process: resize all source images to max 4000px on longest edge
# using sips (built into macOS, no install needed)
for f in ~/Desktop/batch-input/*.jpg; do
sips --resampleHeightWidthMax 4000 "$f" --out ~/Desktop/batch-ready/
done
AppleScript and Affinity Photo: The Gap
This is the question everyone asks: "Can I script Affinity Photo with AppleScript?"
No. Affinity Photo does not expose an AppleScript dictionary. Running tell application "Affinity Photo 2" will launch the app but gives you zero commands beyond activate, open, and quit. You cannot create documents, apply filters, export, or do anything useful.
-- This is the entirety of what AppleScript can do with Affinity Photo
tell application "Affinity Photo 2"
activate
open POSIX file "/Users/you/photo.jpg"
end tell
-- That's it. No further commands available.
Serif (the company behind Affinity) has acknowledged the demand for scripting support in their forums since 2015. As of Affinity Photo 2.6 (early 2026), there is still no scripting API, no plugin SDK, and no public timeline for either.
Driving Affinity Photo Through the Accessibility API
When an app has no scripting interface, the macOS accessibility API becomes the automation layer. Every button, menu item, text field, and slider in Affinity Photo is exposed as an AXUIElement in the accessibility tree. An external program can read these elements, click them, type into them, and verify results.
This is how AI desktop agents automate apps that were never designed to be automated.
A Python script using pyobjc can enumerate every menu item in Affinity Photo and click them programmatically:
import subprocess
import ApplicationServices as AS
def get_affinity_pid():
out = subprocess.check_output(["pgrep", "-f", "Affinity Photo"])
return int(out.strip().split()[0])
def click_menu(app_ref, menu_name, item_name):
"""Navigate Affinity Photo's menu bar via accessibility API."""
menu_bar = AS.AXUIElementCopyAttributeValue(
app_ref, "AXMenuBar", None
)[1]
# Walk the menu tree to find and press the target item
for menu in AS.AXUIElementCopyAttributeValue(menu_bar, "AXChildren", None)[1]:
title = AS.AXUIElementCopyAttributeValue(menu, "AXTitle", None)[1]
if title == menu_name:
items = AS.AXUIElementCopyAttributeValue(menu, "AXChildren", None)[1]
for item in items:
item_title = AS.AXUIElementCopyAttributeValue(
item, "AXTitle", None
)[1]
if item_title == item_name:
AS.AXUIElementPerformAction(item, "AXPress")
return True
return False
pid = get_affinity_pid()
app = AS.AXUIElementCreateApplication(pid)
click_menu(app, "Filter", "Gaussian Blur...")
This approach has real tradeoffs:
Building a Batch Workflow with an AI Desktop Agent
The most flexible approach combines an AI agent that can see and interact with the desktop with Affinity Photo's existing UI. Instead of writing fragile coordinate-based scripts, an AI agent uses the accessibility tree to understand what is on screen and decide what to click.
Here is a concrete workflow for batch watermarking 200 product photos:
- Agent reads the source folder, gets the list of
.jpgfiles - For each file, agent tells Affinity Photo to open it (File > Open or
Cmd+O) - Agent navigates to the watermark macro in the Macro panel and runs it
- Agent triggers Export (File > Export), selects PNG, sets the output path
- Agent closes the file without saving the original
- Repeat for the next file
The agent handles the orchestration that macros cannot: file iteration, error recovery (if a file fails to open, skip it and log), and cross-app coordination (move processed files to an archive folder).
Tip
Combine Affinity's built-in Batch Job for the heavy pixel work (it runs filters at native speed) with an AI agent for the orchestration layer. The agent sets up the batch, monitors progress, and handles post-processing. You get the speed of native macros with the flexibility of an agent.
Keyboard Shortcuts as an Automation Surface
Every tool, filter, and operation in Affinity Photo can be assigned a keyboard shortcut (Edit > Preferences > Keyboard Shortcuts). This turns the app into something quasi-scriptable: if you can send keystrokes, you can drive Affinity Photo.
Useful shortcuts to assign for automation:
Cmd+Shift+E → Export dialog
Cmd+Shift+W → Batch watermark macro (custom)
Cmd+Shift+R → Resize to 1200px macro (custom)
Cmd+Option+S → Save as TIFF
Cmd+W → Close document (built in)
Then from a shell script, use osascript to send keystrokes:
#!/bin/bash
# Send Cmd+Shift+E to Affinity Photo to open the Export dialog
osascript -e '
tell application "System Events"
tell process "Affinity Photo 2"
keystroke "e" using {command down, shift down}
end tell
end tell
'
This is crude but effective for linear sequences. It falls apart when you need to wait for a dialog to appear, read values from the UI, or handle errors. That is where accessibility-based agents add value: they can wait for specific UI elements to appear before proceeding.
Common Pitfalls
-
Recording macros with wrong document state. Macros capture absolute operations, not relative ones. If you record a "resize to 1200px" macro on a 4000px image, it works. Record it on a 800px image and it still resizes to 1200px (upscaling). Use "Resize Document" with percentage mode in the macro if you want relative scaling.
-
Batch Job silently skipping files. Affinity's batch processor skips files it cannot open (corrupted JPEGs, unsupported RAW formats) without logging errors. Always compare input file count to output file count after a batch run:
ls ~/input/ | wc -lvsls ~/output/ | wc -l. -
Accessibility permissions not granted. On macOS 13+, the accessibility API requires explicit permission per app. If your script or agent cannot find any UI elements, check System Settings > Privacy & Security > Accessibility. The controlling app (Terminal, Python, your agent) must be in the allowed list.
-
UI timing assumptions. Filters in Affinity Photo take variable time depending on image size and complexity. A Gaussian blur on a 50MP file might take 2 seconds; on a 2MP file, 50ms. Never use fixed
sleepdelays. Poll the accessibility tree for the dialog to close or the progress bar to disappear. -
Macro panel not visible. Macros will not appear in the batch processor if the Macro panel has never been opened in the session. Open View > Studio > Macro at least once before running batch jobs, or the macro dropdown will be empty.
Checklist: Picking the Right Automation Approach
Use this decision tree based on what you actually need:
| Your situation | Best approach |
|---|---|
| Same edits on a folder of images, no conditions | Affinity Macros + Batch Job |
| Pre/post processing (resize, rename, convert) | sips or ImageMagick in a shell script |
| Conditional edits based on filename or metadata | AI desktop agent + Affinity macros |
| Cross-app workflow (edit in Affinity, upload to CMS) | AI desktop agent |
| Real-time folder watching (process on arrival) | macOS Folder Actions + shell script + Batch Job |
| Full pipeline (download, edit, watermark, export, upload) | AI desktop agent orchestrating everything |
Minimal Working Example: Batch Resize and Export
This shell script combines sips for the resize step with Affinity Photo's batch processor for quality-sensitive operations:
#!/bin/bash
# batch-process.sh
# Resize images to max 2400px, then open in Affinity for final adjustments
INPUT_DIR="$HOME/Desktop/photos-raw"
RESIZED_DIR="$HOME/Desktop/photos-resized"
OUTPUT_DIR="$HOME/Desktop/photos-final"
mkdir -p "$RESIZED_DIR" "$OUTPUT_DIR"
# Step 1: Resize with sips (fast, no quality loss for downscale)
for f in "$INPUT_DIR"/*.{jpg,jpeg,png,tiff}; do
[ -f "$f" ] || continue
sips --resampleHeightWidthMax 2400 "$f" --out "$RESIZED_DIR/"
done
echo "Resized $(ls "$RESIZED_DIR" | wc -l | tr -d ' ') files"
echo "Now open Affinity Photo > File > New Batch Job"
echo " Source: $RESIZED_DIR"
echo " Macro: your-export-macro"
echo " Output: $OUTPUT_DIR"
# Step 2: Open Affinity Photo to the batch dialog
open -a "Affinity Photo 2"
For full hands-free operation, replace Step 2 with an AI agent that opens the Batch Job dialog, configures it, and clicks OK. The agent watches the output folder and verifies file counts when processing completes.
Wrapping Up
Affinity Photo's lack of a scripting API is its biggest gap for professional workflows. The built-in macro system handles simple replay well, but anything involving file iteration, conditional logic, or cross-app coordination requires external tooling. The accessibility API is the universal escape hatch: it lets scripts and AI agents drive Affinity Photo through the same interface a human uses, no plugin required.
Fazm is an open source macOS AI agent that automates desktop apps through the accessibility API. Open source on GitHub.