User manual for Finishing Touches
Contents
- Install
- Your first rule
- Match by aspect ratio
- Resize before running actions
- Chain multiple actions
- Run multiple rules on one image
- More specific rules with modifiers
- Rules without keywords
- Automatic color and B&W detection
- Workflow examples
- Creating rules: dialog vs direct editing
- Complete rule syntax
- How matching works
- Runtime variables
- Custom variables
- Writing IPTC metadata
- Customizing the script
- Tips and best practices
- TextMate support
- FinishingChunks — better than droplets on macOS
Install
Copy the script
Copy FinishingTouches.jsx to Photoshop’s scripts folder:
- Mac:
/Applications/Photoshop 202x/Presets/Scripts - Windows:
C:\Program Files\Adobe\Adobe Photoshop 202x\Presets\Scripts
Restart Photoshop and confirm Finishing Touches appears in File > Automate.
Load the actions
In the Actions palette, click the hamburger icon (≡), choose Load Actions…, select all .atn files, and click Open.
Your first rule
A rule tells the script: “When you see this keyword, run these actions.”
keywords: export | actions: Sharpen for web @ My Actions
This means: for any image with the keyword export, run the action Sharpen for web from the action set My Actions.
That’s all you need to start. Keywords come from your image’s IPTC/EXIF metadata — set them in Lightroom, Bridge, or Photoshop via File > File Info > Keywords.
Match by aspect ratio
Add ratio: to run a rule only for images of a specific shape:
keywords: instagram | ratio: 1x1 | actions: Square crop @ Social
keywords: instagram | ratio: 2x3 | actions: Portrait crop @ Social
The script detects which ratio the image is closest to and applies the matching rule.
Available aspect ratios: 1x1, 6x7, 4x5, 4x3, 2x3, 16x9, xpan and 17x6
Resize before running actions
Add size: to resize the image before actions run. The value is always the short side in pixels:
keywords: web | ratio: 2x3 | size: 2048 | actions: Web sharpen @ Export
A portrait at 2048 short side becomes 2048 × 3072 pixels.
Chain multiple actions
Separate actions with ; to run several in sequence:
keywords: portfolio | ratio: 2x3 | size: 3600 | actions: Film look @ Color ; Edge glow @ Effects ; Sharpen @ Export
Actions run left to right.
Run multiple rules on one image
Tag an image with multiple keywords to run multiple rules. For example, tag with film, grain, and export to run all three rules in turn.
Controlling the order
Use the Keyword order field to set which rules run first:
film;grain;borders;export
All images process in this order regardless of what order keywords appear in the file.
Example:
- Image keywords:
export,grain,film - Keyword order:
film;grain;export - Processing order: film → grain → export ✓
More specific rules with modifiers
Add extra keywords to make a rule more specific. The first keyword is the master keyword (must appear in Keyword order). Additional keywords are modifiers:
keywords: grain | actions: Light grain @ Effects
keywords: grain ; heavy | actions: Heavy grain @ Effects
- Image tagged
grainonly → Light grain - Image tagged
grainandheavy→ Heavy grain (more specific wins)
Example: ISO variants
keywords: film | actions: Normal grain @ Film
keywords: film ; iso1600 | actions: ISO 1600 grain @ Film
keywords: film ; iso3200 | actions: ISO 3200 grain @ Film
The script automatically picks the most specific rule that matches the image’s keywords.
Rules without keywords
Rules can match all images of a certain aspect ratio, independent of keywords:
ratio: 16x9 | size: 3840 | actions: Panorama crop @ Formats
Or match all images with a keyword, regardless of ratio:
keywords: logo | actions: Add watermark @ Branding
Automatic color and B&W detection
Enable Automatically Add Keywords ‘color’ or ‘bw’ in the dialog options. The script inspects each image and temporarily adds a virtual keyword (color or bw) for matching — without writing it to file metadata.
This lets you write rules like:
keywords: color | ratio: 2x3 | actions: Color film look @ Presets
keywords: bw | ratio: 2x3 | actions: B&W film look @ Presets
Workflow examples
Film photography
Goal: Apply film emulation, halation, and grain in order.
Keyword order: film;halation;grain;export
keywords: film ; portra400 | ratio: 2x3 | actions: Portra 400 look @ Film Emulation
keywords: film ; tri-x | ratio: 2x3 | actions: Tri-X look @ Film Emulation
keywords: halation | ratio: 2x3 | actions: Halation effect @ Effects
keywords: grain | actions: 35mm grain @ Texture
keywords: export | ratio: 2x3 | size: 3600 | actions: Sharpen and save @ Export
Tag an image with film, portra400, halation, grain, export → Portra look → Halation → Grain → Export.
Social media exports
Goal: Multiple output sizes from one source.
keywords: instagram | ratio: 1x1 | size: 1080 | actions: Square crop @ Social ; IG sharpen @ Social
keywords: instagram | ratio: 4x5 | size: 1080 | actions: Portrait crop @ Social ; IG sharpen @ Social
keywords: facebook | size: 2048 | actions: FB watermark @ Social ; FB sharpen @ Social
Tag with both instagram and facebook — both rules run.
Print vs web
Goal: Different sharpening for print and web from the same master.
keywords: print | ratio: 2x3 | size: 4800 | actions: Print sharpen @ Output
keywords: web | ratio: 2x3 | size: 2048 | actions: Web sharpen @ Output
Tag with both print and web — two versions are created.
Color vs black-and-white
Enable Automatically Add Keywords ‘color’ or ‘bw’ option, then:
keywords: color | ratio: 2x3 | actions: Color correction @ Grading
keywords: bw | ratio: 2x3 | actions: BW conversion @ Grading
keywords: grain ; color | actions: Color grain @ Texture
keywords: grain ; bw | actions: BW grain @ Texture
No manual color/bw tagging needed — the script detects it automatically.
Creating rules: dialog vs direct editing
There are two ways to create rules:
- Using the Rule Builder dialog (recommended as a starting point)
- Editing rules directly as text (for advanced or bulk editing)
The Rule Builder dialog
Open an image, run Finishing Touches from the Actions panel, and the Rule Builder dialog appears.
- Left column: Compose one rule at a time. Enter keywords, select an optional aspect ratio, specify an optional
size, and choose one or more actions. Click + Add Keyword to append a keyword to the current rule. Choose an action from the dropdown and click + Add Action to add it; actions execute in the order they appear. When the rule looks correct, click Add to Rules to append it to the recipe on the right. - Right column: The full recipe text. Paste or edit rules directly here. Click Save settings to store the recipe inside the action so droplets and recorded actions replay the exact configuration.
Dialog options
- Keyword order — Sets processing priority when an image has several matching keywords.
- Save and Close when Actions are Completed — Saves and closes each document after finishing (required for droplets and unattended batches). Keep disabled while building or testing rules.
- Automatically Add Keywords ‘color’ or ‘bw’ — Inspects each image and adds a temporary virtual keyword for matching.
- Save TIFF as JPG — Converts TIFF files to JPEG on save (optionally into a
/jpgssubfolder). - Convert Image to sRGB — Converts color images to sRGB IEC61966-2.1 before writing JPEGs.
- Remove Matched Keywords from Image — Deletes matched keywords after processing to clean up metadata.
Saving and batch processing
- Open an image.
- Run Finishing Touches from the Actions panel.
- Build your rules and click Save settings to store the configuration in the action.
- When ready to batch-process, enable Save and Close when Actions are Completed, save the action, and create a droplet from it.
Editing rules as text
For bulk editing or pasting from another source, work directly in the right column of the dialog. You can always switch between building rules in the UI and editing text directly — the script uses whatever is in the recipe field when you save.
Complete rule syntax
keywords: kw1 ; kw2 | ratio: 2x3 | size: 3600 | actions: Action1 @ Set1 ; Action2 @ Set2
keywords:(optional) — One or more keywords separated by;. First is the master keyword, others are modifiers.ratio:(optional) —1x1,6x7,4x5,4x3,2x3,16x9,xpanor17x6.size:(optional) — Target short-side dimension in pixels.actions:(required) —Action name @ Action set name, multiple actions separated by;. The.atnextension is optional for action set names.
If fuzzy_action_whitespace = true in the script, Finishing Touches also ignores whitespace differences when locating actions and action sets at runtime. This is useful when an installed action contains accidental double spaces or similar spacing inconsistencies. Matching is still exact for the actual letters and numbers.
Comments and formatting
- Empty lines are ignored
- Lines starting with
#are comments - Spaces around
|,;, and@are flexible
# Web export rules
keywords: web ; color | ratio: 2x3 | size: 2048 | actions: Color grade @ Web ; Sharpen @ Web
keywords: web ; bw | ratio: 2x3 | size: 2048 | actions: BW conversion @ Web ; Sharpen @ Web
# Social media
keywords: instagram | ratio: 1x1 | size: 1080 | actions: Square crop @ Social
How matching works
When the script processes an image:
- Reads keywords from IPTC/EXIF metadata (File info > Keywords)
- Classifies aspect ratio to one of seven categories
- Optionally adds
colororbwvirtual keyword (if detection enabled) - Reorders keywords according to your Keyword order setting
- Builds runtime variables — including
{keywords}(comma-joined, priority-ordered keyword list), making all runtime variables available forsetconditions - Processes
setstatements — evaluates conditional assignments, builds custom variables - Applies
metastatements — writes IPTC fields to the document with all variables already resolved - For each keyword in order:
- Finds rules whose master keyword matches
- Filters by aspect ratio (if rule specifies one)
- Picks the most specific matching rule (more keywords = more specific; with ratio > without ratio; earlier in list wins ties)
- Resizes (if rule has
size:) - Runs all actions in the rule
- Removes matched keywords (if tidy option enabled)
- Saves and closes (if option enabled)
Runtime variables
Runtime variables let rules adapt to each image’s properties. Use them in any rule field.
{size}— Short edge snapped to the nearest breakpoint (default: 3600, 4800, 6000, 7200). Setsize_round_down = trueto always snap down.{format}— Aspect ratio:1x1,6x7,4x5,4x3,2x3,16x9,xpanor17x6.{mode}— Color mode:colororbw.{orientation}— Image orientation:portrait,landscape, orsquare.{width}— Width in pixels (exact, not rounded).{height}— Height in pixels (exact, not rounded).{bitdepth}— Bit depth per channel:8,16, or32.{year}— Capture year from EXIF DateTimeOriginal, falling back to the current year if no capture date is found.{filename}— Document filename without extension.{keywords}— All document keywords as a comma-separated string, sorted by Keyword order. Best used withcontainsinsetconditions.{make}— Camera manufacturer from EXIF (e.g.Nikon,Canon). Empty string if not available.{model}— Camera model from EXIF (e.g.NIKON Z9). Empty string if not available.{lens}— Lens model from EXIF auxiliary metadata (e.g.24-70mm f/2.8). Empty string if not available.
Dynamic action names
Instead of separate rules for each size:
keywords: grain | ratio: 2x3 | size: {size} | actions: 35mm ISO 400 ({size} Color) @ Grain {size}
For a 6400px image this becomes:
keywords: grain | ratio: 2x3 | size: 6000 | actions: 35mm ISO 400 (6000 Color) @ Grain 6000
Format-agnostic rules
keywords: export | ratio: {format} | size: 2048 | actions: Sharpen {format} @ Export
Orientation-specific borders
keywords: border | actions: Add {orientation} border @ Borders
Custom variables
For more complex workflows, create your own variables using set statements. All set statements must be placed at the top of your recipe, before any rules. The script processes them in order, then substitutes the results into your rules.
Basic syntax
set variable_name = value
set variable_name = value when condition
set variable_name = value when condition else fallback_value
set variable_name = value1 when condition1 else value2 when condition2 else fallback_value
Clauses are evaluated left to right. The first matching when wins. A bare value at the end with no when is the unconditional default.
The else fallback form is equivalent to two separate lines, but more compact:
# These two are identical:
set FG_mode = Color when {mode} == color else Monochrome
set FG_mode = Monochrome
set FG_mode = Color when {mode} == color
Use chained form when cases are mutually exclusive. Use separate set lines when later assignments should layer on top of earlier ones.
Variable naming
- Must start with a letter or underscore
- Can contain letters, numbers, and underscores
- Case-sensitive:
Grainandgrainare different
Defining and using variables
Use plain names when defining; use {curly braces} when referencing:
set grain = heavy grain
set output_size = 4096
keywords: process | actions: Apply {grain} @ Effects
keywords: export | size: {output_size} | actions: Sharpen @ Export
Conditions and operators
Comparison operators: == (or =), !=, <, >, <=, >=
List membership (== and != only):
set my_format = wide when {format} == [16x9, 21x9]
set standard = yes when {format} == [2x3, 4x5, 1x1]
set needs_special = yes when {format} != [2x3, 4x3]
Lists can contain any values separated by commas; spaces around values are trimmed.
contains / not contains — test whether {keywords} includes a specific keyword:
set treatment = dark when {keywords} contains halation
set safe = yes when {keywords} not contains overexposed
Matching is exact and case-sensitive. Cannot be combined with and / or in the same sub-expression — use separate set lines for complex logic.
Logical operators: and (higher precedence than or), or
set grain = heavy when {size} >= 6000 and {format} == 2x3
set treatment = special when {mode} == color or {bitdepth} == 16
Use multiple set statements instead of parentheses for complex logic.
Numeric comparisons: Numbers are compared mathematically — 3600 < 4800 is true.
String comparisons: Compared lexicographically. Values with spaces work fine: set action_name = light grain when {size} < 4800
Multiple assignments to the same variable
The last matching condition wins. Always provide a default first:
set grain = medium
set grain = light when {size} < 4800
set grain = heavy when {size} >= 6000
- 3600px →
grain = light - 5000px →
grain = medium - 7200px →
grain = heavy
Examples
Size-based processing tiers:
set tier = base
set tier = standard when {size} >= 4800
set tier = premium when {size} >= 6000
keywords: export | size: {size} | actions: Grade {tier} @ Color ; Sharpen @ Export
Format-specific naming:
set format_name = square when {format} == 1x1 else medium format when {format} == 6x7 else large format when {format} == 4x5 else 35mm portrait when {format} == 2x3 else widescreen when {format} == 16x9 else panoramic when {format} == xpan else unknown format
keywords: export | actions: Process {format_name} @ Workflows
Bit depth handling:
set workflow = standard
set workflow = high bit depth when {bitdepth} == 16
set workflow = float precision when {bitdepth} == 32
keywords: process | actions: {workflow} processing @ Advanced
Orientation-dependent effects:
set border_size = medium
set border_size = wide when {orientation} == landscape
set border_size = narrow when {orientation} == portrait
set vignette = standard vignette
set vignette = wide vignette when {orientation} == landscape
keywords: borders | actions: Add {border_size} border @ Borders
keywords: vignette | actions: Apply {vignette} @ Effects
Keyword-based branching with contains:
set halation_action = none
set halation_action = Add halation when {keywords} contains halation
set grain = heavy grain
set grain = light grain when {keywords} contains soft
keywords: film | actions: Apply {grain} @ Texture ; Apply {halation_action} @ Color
Complex conditional with logical operators:
set treatment = standard
set sharpening = normal
set treatment = premium when {size} >= 6000 and {mode} == color
set sharpening = extra sharp when {size} >= 6000 and {mode} == color
set aspect_treatment = wide format when {format} == 4x5 or {format} == xpan
keywords: process | actions: Process with {treatment} @ Workflows ; Apply {sharpening} @ Export
Complex multi-variable recipe:
set grain_amount = medium grain
set grain_action = Apply to layer
set grain_amount = light grain when {size} < 4800
set grain_amount = heavy grain when {size} >= 6000
set grain_action = Apply and blend when {format} == xpan
set grain_amount = panoramic grain when {format} == xpan
set grain_type = color grain when {mode} == color
set grain_type = bw grain when {mode} == bw
keywords: grain | actions: {grain_action} {grain_amount} {grain_type} @ Texture
List membership:
set format_type = standard when {format} == [2x3, 3x2, 4x3, 3x4]
set format_type = square when {format} == [1x1]
set format_type = wide when {format} == [16x9, 21x9, xpan]
set format_type = medium format when {format} == [6x7, 4x5, 5x4]
set grain = heavy grain when {format} == [xpan, 16x9] and {size} >= 6000
keywords: process | actions: Process {format_type} @ Formats
Debugging
Set debug = true in the script (line 56) to emit per-image diagnostics. ✓ means the clause was applied; ✗ means it did not apply (may include (matched earlier) when a later clause evaluated true but an earlier one already applied). Chained else ... when expressions are grouped together — all clauses for that variable appear under one heading and the first matching when wins.
Custom variables (set statements):
{grain} = medium
[✓] medium (default)
{grain} = (unset)
[✗] light grain when {size} < 4800
{grain} = heavy grain
[✓] heavy grain when {size} >= 6000
Debug output follows evaluation order: runtime variables first, then set statements in recipe order, then variable substitution into rules, then rule execution.
Error handling
If a {variable} is never defined, it remains literal and will cause Photoshop to fail when the script tries to locate an action or metadata field with that name. Always provide an unconditional default:
# Safe: always defined
set my_action = default action
set my_action = special action when {mode} == bw
# Risky: if size is 5000, neither condition matches → {grain} stays as {grain}
set grain = light when {size} < 4800
set grain = heavy when {size} >= 6000
Fix — option 1: Add an unconditional default on its own line before the conditionals.
Fix — option 2: Use a chained else ... when whose final clause is an unconditional fallback:
set mode_label = color when {mode} == color else mono
set iso = 1600 when {keywords} contains iso1600 and {mode} == color else 3200 when {keywords} contains iso3200 and {mode} == bw else 400
Limitations
- Custom variables cannot reference other custom variables — only runtime variables
- No mathematical expressions (
{size} + 100doesn’t work) - No parentheses for grouping logical expressions
- List membership (
[ ]) works with==and!=only — not<,>,<=,>= contains/not containscannot be combined withand/orin the same sub-expression; use separatesetlines for complex logic- Avoid values or condition text that literally contain ` else ` (with surrounding spaces), as the parser splits on every occurrence
- Avoid keyword names that literally contain ` and ` or ` or ` (with spaces) when using
contains elsewithout a precedingwhen(set foo = bar else baz) makesbazunreachable —baris an unconditional default and wins immediately- All values are strings (numbers are treated as text after assignment)
Writing IPTC metadata
Use meta statements to write IPTC/XMP metadata fields to the document. Place them in the recipe alongside rules and set statements. All variable placeholders are resolved before meta lines are applied.
meta author = Joakim Hertze
meta copyrightNotice = © {year} Joakim Hertze
meta creatorWebsite = https://hertze.se
meta creatorEmail = my_funny@email.com
meta creatorCity = Staffanstorp
meta creatorCountry = Sweden
Note: Setting copyrightNotice automatically sets the copyright status to Copyrighted. To use a different status, add a meta copyrightStatus line after it — publicDomain sets it to false, unknown removes it entirely.
Supported fields
title— Document titleauthor— Creator / photographer nameauthorPosition— Creator’s job titlecaption— Description / captioncaptionWriter— Caption writercategory— IPTC category (three letters)city— CityprovinceState— Province or statecountry— Countrycredit— Credit linesource— Sourceheadline— Headlineinstructions— Special instructionsjobName— Job identifiertransmissionReference— Transmission referencecopyrightNotice— Copyright text (also sets status to Copyrighted)copyrightStatus—copyrighted,publicDomain, orunknownownerUrl— URLsupplementalCategories— Supplemental categories (semicolon-separated)
Contact info fields
IPTC contact info is written to the correct XMP struct automatically:
creatorAddress— Street addresscreatorCity— CitycreatorState— State or provincecreatorPostalCode— Postal / ZIP codecreatorCountry— CountrycreatorPhone— Phone number(s)creatorEmail— Email address(es)creatorWebsite— Website URL(s)
Customizing the script
Edit these variables at the top of FinishingTouches.jsx (lines 33–55):
// Color mode keywords
var color_keyword = "color";
var bw_keyword = "bw";
// Format keywords for aspect ratios
var format_1x1 = "1x1";
var format_6x7 = "6x7";
var format_4x5 = "4x5";
var format_4x3 = "4x3";
var format_2x3 = "2x3";
var format_16x9 = "16x9";
var format_xpan = "xpan";
// Orientation keywords
var portrait_keyword = "portrait";
var landscape_keyword = "landscape";
var square_keyword = "square";
// Size breakpoints for {size} variable
var size_breakpoints = [3600,4800,6000,7200];
// If true, {size} snaps down to the nearest breakpoint below the short edge.
// If false (default), {size} snaps to the nearest breakpoint (up or down).
var size_round_down = false;
// When processing large or high-resolution files in batches, GPU memory (VRAM) can gradually fill, sometimes leaving colored blocks or mosaic-like tiles in saved files.
// Enabling safe_mode = true triggers an automatic Purge -> All between steps, preventing these artifacts.
// This stabilizes output but slows processing, as Photoshop must reload data after each purge.
var gpu_safe_mode = true;
// If true, action and action set lookup ignores whitespace differences
// and an optional .atn suffix on action set names.
var fuzzy_action_whitespace = true;
// Debug mode
var debug = false;
Why customize:
- Prefer different labels: Change
format_2x3 = "35mm"if you prefer that name. - Different size tiers: Adjust
size_breakpointsfor your workflow. - Breakpoint snapping: Set
size_round_down = trueto always snap{size}down to the nearest breakpoint below the short edge. The default snaps to the nearest breakpoint whether above or below. - Action-name tolerance: Keep
fuzzy_action_whitespace = trueif you want Finishing Touches to tolerate extra, missing, or repeated spaces in action names and action set names. Turn it off if you want strict exact-name matching. - Localization: Use keywords in your language.
- Debugging: Set
debug = trueto see diagnostic alerts.
All these values are used throughout the script and in runtime variables like {format}, {mode}, and {orientation}.
Tips and best practices
- Avoid actions that save and close — the script manages saving. Actions that close the document will interrupt processing.
- Keep keyword combinations unique — if multiple rules have identical keywords and ratios, only the first runs.
- Use comments — start lines with
#to document your rules. - Test incrementally — start with simple rules, test them, then add complexity.
- Update action names carefully — if you rename actions in Photoshop, update your rules to match. With
fuzzy_action_whitespace = true, spacing differences are tolerated, but actual name changes are not. - Use Keyword order strategically — put foundational rules first (film emulation), finishing rules last (export).
- Tag in Lightroom or Bridge — the normal workflow is: tag → export → drop on droplet → done.
TextMate support
The tools/ folder contains a TextMate bundle (FinishingTouches.tmbundle) that adds syntax highlighting and a recipe validator for .ftr files.
Installing the bundle
Double-click FinishingTouches.tmbundle in Finder — TextMate installs it automatically. Restart TextMate if it is already running.
Syntax highlighting and validation
Once the bundle is installed, any file with the .ftr extension is recognised as a Finishing Touches Recipe. The bundle includes a Validate Recipe command. Run it from the Bundles ▸ Finishing Touches menu (or assign a keyboard shortcut in the bundle editor). Results are shown in a new window.
FinishingChunks — better than droplets on macOS
FinishingChunks is a companion macOS utility for when you need to run Photoshop actions on a large folder of images. It processes images in small batches, restarting Photoshop between each batch to keep memory and GPU buffers under control. This leads to less scratch disc use, which may speed up batching on your setup. You use it exactly like you would a Photoshop droplet, including directly during Lightroom exports.
Building your own app from the source
You’ll find the AppleScript file FinishingChunks.applescript in the tools/FinishingChunks/ folder. You use this to build your own app.
- Double-click on FinishingChunks.applescript to open it in Script Editor (otherwise found in /Applications/Utilities/).
- Edit
actionSetandactionNameto match the Photoshop action you want to run (see below). - Choose File > Export…
- Set File Format to Application.
- Make sure Stay open after run handler is unchecked and Startup screen is unchecked. Code signing is optional.
- Save it with a name of your choosing.
How to use it
Double-click on your app to launch it and pick a folder, or drag a folder (or individual files) straight onto it. The script finds all matching images, launches the Photoshop version you specified, runs the chosen Action on each file, restarts Photoshop between chunks, and writes a log next to the script.
When used as a droplet from Lightroom or other apps that send files one by one, incoming files are queued and picked up by a single background worker. That avoids AppleScript timeout errors while exports are still arriving.
First run — macOS security
The first time you run the app you will need to grant permission to control the Photoshop.
On recent macOS versions you may also need to grant the app access to protected folders such as Desktop or Documents. If macOS blocks access, open System Settings > Privacy & Security and enable access for your app under Files and Folders, or grant Full Disk Access if needed.
If macOS reports the app as broken, do this:
- Go to System Settings > Privacy & Security.
- Scroll down to the security section — you should see a message about FinishingChunks.app being blocked.
- Click Open Anyway, then confirm.
Settings
Open FinishingChunks.applescript and edit the properties at the top to match your setup:
appName— the exact installed Photoshop version to automate, e.g."Adobe Photoshop 2026". This controls which Photoshop copy the script launches, talks to, checks, quits, and force-quits if needed.actionSet/actionName— the Action Set and Action name exactly as they appear in Photoshop’s Actions panel.fileEndings— which file types to pick up. Default:jpg,jpeg,tif,tiff,png,psd,psb. Add or remove entries to match your exports.chunkSize— how many images to process before restarting Photoshop.enableLog— set tofalseto disable the written log.logFailuresOnly— whentrue, the log skips per-fileoklines and only writes failures, chunk messages, and the session summary.logFileName— the log file name written next to the app.forceQuitBetweenChunks— whentrue, Photoshop is force-quit for chunk resets instead of being asked to quit cleanly first.
Timing settings
If Photoshop is slow to open files or your Action takes a long time to save and close, increase these values:
appLaunchTimeout— how long to wait for Photoshop itself to start (default 30 s).appQuitTimeout— how long to wait for Photoshop to quit cleanly before the script falls back to a force-quit (default 15 s). This matters whenforceQuitBetweenChunksisfalse.waitForDocumentOpenTimeout— how long to wait for an image to open (default 30 s). Raise to 30–60 s or more for large files.waitForDocumentCloseTimeout— how long to wait after the Action finishes before moving on (default 30 s). Raise to 30–120 s if your Action saves or exports.
Advanced queue timing
These lower-level settings mainly matter when the app is used like a droplet and files arrive one by one from Lightroom or another exporter:
internalQueueSettleDelay— how long FinishingChunks waits after a queued drop event before it drains the queue (default 3 s). This short pause lets more incoming files arrive so they can be processed as one batch instead of repeatedly starting tiny batches.internalQueueHeartbeatTimeout— how old the queue worker heartbeat is allowed to be before the script treats the queue lock as stale and recovers it (default 180 s). This helps the next launch recover if an earlier worker crashed or was force-quit while holding the queue lock.
You normally do not need to change these values unless you are tuning droplet-style workflows or recovering from stuck queued jobs.
Files written by the script
FinishingChunks writes a few runtime files besides your processed images:
finishingchunks.logby default, or whatever you set inlogFileName— written next to the app or script itself. Purpose: session logging, per-file status lines, chunk boundaries, failures, and the final summary./tmp/finishingchunks-<app-hash>.queue— a temporary queue file used when files are dropped one at a time and need to be buffered for a single worker. Purpose: store pending dropped file paths until the queue worker starts processing them./tmp/finishingchunks-<app-hash>.queue.batch— a temporary handoff file created while the worker drains the queue. Purpose: atomically move the current queue aside, read that batch, then delete it./tmp/finishingchunks-<app-hash>.lock/— a temporary lock directory used to make sure only one queue worker is active per app copy. Purpose: prevent two overlapping workers from processing the same queued items./tmp/finishingchunks-<app-hash>.lock/heartbeat— a heartbeat file inside the lock directory. Purpose: updated while work is in progress so a later launch can tell whether the lock is still alive or stale.
The <app-hash> part is generated from the app’s own path, so separate copies of the app get separate queue and lock files.
These /tmp files are transient working files. They are used only for the queueing system, mainly in droplet-style one-file-at-a-time workflows, and are recreated automatically as needed.
Notes
- You can make multiple copies of the app; each copy runs its own action.
- Files are scanned recursively — subfolders are always included.
- Files are processed in alphabetical order, grouped by folder path.
- Hidden files such as
.DS_Storeare ignored automatically. - By default the script force-quits Photoshop between chunks for a hard reset. If
forceQuitBetweenChunksisfalse, it tries a normal quit first and only force-quits if Photoshop does not exit within the configured timeout. - If you get “No files found”, check that the folder contains images in a supported format. Folders on the Desktop, Documents, or other protected locations may require you to grant macOS access to the script.