SESSION 16 / 20 Automation Script-Fu

Script-Fu Basics

Every repetitive task you do manually in GIMP can be automated. Script-Fu is GIMP's built-in scripting language — a dialect of Scheme — and the Script-Fu Console lets you run commands, inspect results, and build reusable scripts without leaving the application.

1h Session Length
+15m Review
4 Phases
💡
0:00 – 0:10  ·  ⏱ 10m
Concept Brief — Why Scripting Pays Off
▼

The 80/20 case for scripting is simple: any task you perform more than five times is a candidate for automation. Resizing a folder of images, adding a watermark to every export, converting a batch of PNGs to JPEG at a consistent quality setting, flattening and exporting every layer of a multi-layer document — these are manual operations that take minutes per file and hours per folder. A Script-Fu script that does the same job runs in seconds.

Script-Fu is a dialect of Scheme, a member of the Lisp family of languages. Its syntax looks unusual if you have not encountered Lisp before: every operation is wrapped in parentheses, and the function name comes first, before its arguments. This is called prefix notation. Once you read a few examples it becomes natural. You do not need to understand Scheme deeply to use Script-Fu productively — GIMP exposes every menu operation as a named procedure, and combining a handful of them covers 90% of automation tasks. This session teaches that core vocabulary and the structure needed to write a complete, reusable script.

✅

Skill unlock: After this session you'll be able to use the Script-Fu Console to inspect and manipulate the current image, write a complete batch-processing script that operates on every file in a folder, and save scripts to GIMP's scripts directory so they appear in the Filters menu permanently.

  • 01
    Open the Script-Fu Console

    Go to Filters → Script-Fu → Console. The console has two areas: a large output pane at the top that shows results and errors, and a single-line input field at the bottom where you type commands. Press Enter to run a command. Use the Up Arrow key to recall previous commands — essential when iterating on a script. The console is a live interpreter: every command runs immediately against the current GIMP state. Open an image before experimenting so there is a target to operate on.

    Filters → Script-Fu → Console
  • 02
    Understand the Syntax: Prefix Notation and Parentheses

    Every Script-Fu expression is a list in parentheses. The first element is the function name; the remaining elements are its arguments. To get the current image, type (car (gimp-file-load RUN-NONINTERACTIVE "/path/to/file.jpg" "file.jpg")) — but more practically, if an image is already open, use (car (gimp-display-get-image (car (gimp-display-list)))) to get its ID. The pattern (car (some-function ...)) is ubiquitous: most GIMP procedures return a list, and car extracts the first element. Assign the image ID to a variable with (let* ((image (car (gimp-file-load ...))) ...) ...) — the let* form defines local variables for the duration of the expression.

    (car (gimp-version)) ; Returns the GIMP version string — a safe first command to test the console
  • 03
    Inspect the Current Image and Drawable

    Two variables are the foundation of almost every Script-Fu operation: the image (the document, analogous to the .xcf file) and the drawable (the active layer within that image). Get them with the following commands in the console. Run each line separately and observe the integer IDs returned — every GIMP object (image, layer, channel, path) has a unique integer ID used to reference it in scripts.

    (car (gimp-display-get-image (car (gimp-display-list)))) ; Returns the active image ID, e.g. 1 (car (gimp-image-get-active-drawable (car (gimp-display-get-image (car (gimp-display-list)))))) ; Returns the active drawable (layer) ID
  • 04
    Run a Simple Operation: Scale the Active Image

    With an image open, scale it to 800 px wide (maintaining aspect ratio) from the console. The procedure gimp-image-scale-full takes an image ID, width, height, and interpolation type. To scale proportionally, calculate the new height first or use gimp-image-scale which respects the current aspect ratio constraint. After scaling, flatten and export. The workflow below demonstrates the full chain — load, scale, export, delete from memory — which is exactly what a batch script repeats for every file in a folder.

    (let* ((image (car (gimp-file-load RUN-NONINTERACTIVE "/path/to/input.jpg" "input.jpg"))) (drawable (car (gimp-image-get-active-drawable image)))) (gimp-image-scale-full image 800 600 INTERPOLATION-LINEAR) (file-jpeg-save RUN-NONINTERACTIVE image drawable "/path/to/output.jpg" "output.jpg" 0.85 0 0 0 "" 0 1 0 2 0) (gimp-image-delete image))
  • 05
    Use the Procedure Browser to Discover Commands

    GIMP exposes every menu operation as a named Script-Fu procedure. To find the procedure name for any operation, open Filters → Script-Fu → Procedure Browser. Search by keyword — type "sharpen" to find unsharp-mask procedures, "resize" to find scale and resize calls. Each entry shows the procedure name, a description, and its exact argument list with types. This browser is your primary reference — more useful than the official docs for scripting because it reflects the exact procedures available in your installed GIMP version. Whenever you want to script a manual operation, perform it once by hand, then search the Procedure Browser for its name.

    Filters → Script-Fu → Procedure Browser
  • 06
    Write a Batch Script Using cadr and foreach

    A batch script processes every file in a folder by combining cadr (which extracts the file list from a directory listing) with for-each (which iterates over a list). The structure below is the canonical GIMP batch-processing template. Replace the inner operations with any sequence of GIMP procedure calls — sharpening, watermarking, colour adjustments — to build any automated pipeline you need.

    (let* ((filelist (cadr (file-glob "/path/to/folder/*.jpg" 1)))) (for-each (lambda (filename) (let* ((image (car (gimp-file-load RUN-NONINTERACTIVE filename filename))) (drawable (car (gimp-image-get-active-drawable image)))) ; --- your operations here --- (gimp-image-scale-full image 1200 800 INTERPOLATION-LINEAR) (plug-in-unsharp-mask RUN-NONINTERACTIVE image drawable 0.5 0.5 4) ; --- export --- (file-jpeg-save RUN-NONINTERACTIVE image (car (gimp-image-get-active-drawable image)) (string-append "/path/to/output/" (basename filename)) (basename filename) 0.85 0 0 0 "" 0 1 0 2 0) (gimp-image-delete image))) filelist))
  • 07
    Define a Named Script with script-fu-register

    To make a script appear in GIMP's Filters menu permanently, wrap it in script-fu-register and script-fu-menu-register. Save the file with a .scm extension in GIMP's scripts directory (find this path under Edit → Preferences → Folders → Scripts). Restart GIMP (or run (gimp-scripts-refresh) from the console) and the script appears under Filters at the menu path you specified. The script-fu-register call declares the script's name, description, author, copyright, date, image type it accepts, and any user-configurable parameters (strings, integers, colours, file paths) that appear in a dialog when the script is invoked from the menu.

    (define (script-fu-my-resize image drawable target-width) (gimp-image-scale-full image target-width (/ (* (car (gimp-image-height image)) target-width) (car (gimp-image-width image))) INTERPOLATION-LINEAR) (gimp-displays-flush)) (script-fu-register "script-fu-my-resize" "Resize to Width..." "Scales the image to a target width, preserving aspect ratio" "Your Name" "Your Name" "2025" "RGB* GRAY*" SF-IMAGE "Image" 0 SF-DRAWABLE "Drawable" 0 SF-VALUE "Target width (px)" "1200") (script-fu-menu-register "script-fu-my-resize" "<Image>/Filters/My Scripts")
  • 08
    Add a Text Watermark in Script-Fu

    A watermark script is one of the most practically useful scripts to have. It creates a text layer, positions it, sets opacity, flattens, and exports. The key procedures are gimp-text-fontname (creates a text layer), gimp-layer-set-opacity (sets transparency), gimp-image-flatten (merges all layers), and file-jpeg-save or gimp-file-overwrite-png for export. Position the watermark using gimp-layer-set-offsets with coordinates derived from gimp-image-width and gimp-image-height so it scales correctly across different image sizes — place it at 90% of image width minus text layer width for a consistent bottom-right position.

    ; Add a semi-transparent watermark to the active image (let* ((image (car (gimp-file-load RUN-NONINTERACTIVE "/path/img.jpg" "img.jpg"))) (drawable (car (gimp-image-get-active-drawable image))) (text-layer (car (gimp-text-fontname image -1 0 0 "© Your Name" 0 TRUE 36 UNIT-PIXEL "Sans Bold"))) (img-w (car (gimp-image-width image))) (img-h (car (gimp-image-height image))) (txt-w (car (gimp-drawable-width text-layer))) (txt-h (car (gimp-drawable-height text-layer)))) (gimp-layer-set-opacity text-layer 55) (gimp-layer-set-offsets text-layer (- img-w txt-w 20) (- img-h txt-h 20)) (gimp-image-flatten image) (file-jpeg-save RUN-NONINTERACTIVE image (car (gimp-image-get-active-drawable image)) "/path/watermarked.jpg" "watermarked.jpg" 0.85 0 0 0 "" 0 1 0 2 0) (gimp-image-delete image))
⚠️

GIMP 3.2 note: GIMP 3.x introduces a Python-Fu interpreter alongside Script-Fu. For new automation work, Python-Fu (Filters → Script-Fu → Python-Fu Console) is increasingly preferred because Python is more readable and has a larger ecosystem. However, Script-Fu scripts (.scm files) remain fully supported and the majority of scripts shared online are written in Script-Fu. This session uses Script-Fu for maximum compatibility with existing resources.

Prepare a folder called input/ containing 5–10 JPEG images of mixed sizes. You'll write and run a Script-Fu batch script that resizes every image to 800 px wide (preserving aspect ratio) and saves the results to an output/ folder. Create the output folder manually before running — Script-Fu does not create directories.

  • A
    Test with a single file first

    Open the Script-Fu Console. Replace the path below with your actual file path and run it. Confirm the file loads, the scale operation runs without error, and the output file appears in your output folder. Fix any path errors before proceeding to the batch loop — a typo in a path inside a loop can silently fail on every file.

    (let* ((image (car (gimp-file-load RUN-NONINTERACTIVE "/Users/you/input/test.jpg" "test.jpg"))) (orig-h (car (gimp-image-height image))) (orig-w (car (gimp-image-width image))) (new-h (round (* 800 (/ orig-h orig-w))))) (gimp-image-scale-full image 800 new-h INTERPOLATION-LINEAR) (file-jpeg-save RUN-NONINTERACTIVE image (car (gimp-image-get-active-drawable image)) "/Users/you/output/test.jpg" "test.jpg" 0.85 0 0 0 "" 0 1 0 2 0) (gimp-image-delete image) "done")
  • B
    Expand to the full batch loop

    Once the single-file test works, wrap it in the file-glob / for-each pattern. Update both the input glob path and the output directory string. The basename function extracts the filename from a full path so you can reconstruct the output path. Run the full script and verify every file in the input folder appears in the output folder at 800 px wide.

    (let* ((filelist (cadr (file-glob "/Users/you/input/*.jpg" 1)))) (for-each (lambda (filename) (let* ((image (car (gimp-file-load RUN-NONINTERACTIVE filename filename))) (orig-h (car (gimp-image-height image))) (orig-w (car (gimp-image-width image))) (new-h (round (* 800 (/ orig-h orig-w))))) (gimp-image-scale-full image 800 new-h INTERPOLATION-LINEAR) (file-jpeg-save RUN-NONINTERACTIVE image (car (gimp-image-get-active-drawable image)) (string-append "/Users/you/output/" (basename filename)) (basename filename) 0.85 0 0 0 "" 0 1 0 2 0) (gimp-image-delete image))) filelist) (gimp-message "Batch complete."))
  • C
    Verify output and check for errors

    Open one of the output files in GIMP. Confirm it is 800 px wide (Image → Canvas Size or check the title bar). If any file failed, the Script-Fu console will show an error message identifying which filename and which procedure call failed. Common errors: incorrect path separators (use forward slashes on all platforms in Script-Fu), missing output directory, or a source file that is already smaller than 800 px (in which case gimp-image-scale-full upscales it — add a conditional check with if to skip files already at or below the target width).

  • D
    Save the script as a .scm file

    Copy the batch loop into any text editor. Save it as batch-resize.scm in GIMP's scripts folder (find the path at Edit → Preferences → Folders → Scripts — typically ~/.config/GIMP/2.10/scripts/ on Linux/Mac or %APPDATA%\GIMP\2.10\scripts\ on Windows). The next time you open GIMP, the script is available to run from the console or, if you wrap it in script-fu-register, from the Filters menu.

Extend the batch-resize script to also apply Unsharp Mask and stamp a text watermark on each image before exporting. No step-by-step — use the Procedure Browser and the code examples from Phase 2 to combine the techniques.

  • →
    Add plug-in-unsharp-mask inside the batch loop

    After the scale call and before the export call, add a sharpening step. Search the Procedure Browser for plug-in-unsharp-mask to confirm its argument order: it takes run-mode, image, drawable, amount (0.5), radius (0.5), and threshold (4). Call it with RUN-NONINTERACTIVE to suppress any dialog. Remember to refresh the drawable reference after the scale operation — (car (gimp-image-get-active-drawable image)) — because scaling may have changed the layer state.

  • →
    Add a text watermark positioned at the bottom-right

    After sharpening, add a text layer using gimp-text-fontname. Query the image and text layer dimensions using gimp-image-width, gimp-image-height, gimp-drawable-width, and gimp-drawable-height. Calculate the bottom-right position: offset-x = image-width − text-width − 20; offset-y = image-height − text-height − 20. Set the text layer opacity to 55 with gimp-layer-set-opacity. Flatten with gimp-image-flatten before exporting. Test on three files before running the full batch.

💡

Hint: If gimp-image-flatten returns an error after adding the text layer, check that the image mode is RGB (not Indexed). Text layers cannot be flattened in Indexed-colour images. Add (gimp-image-convert-rgb image) before the text layer creation if your source files might be in Indexed or Grayscale mode.

Your progress is saved locally in your browser.
✓   Session 16 complete. Up next: Session 17 — Batch Processing →
🔁
+ 15 minutes

Session 16 Review

15:00 review
Key Concepts
  • Script-Fu uses prefix notation — function name first, then arguments, all wrapped in parentheses
  • Most GIMP procedures return a list; use car to extract the first (usually only) value
  • Every GIMP object — image, layer, channel, path — is referenced by an integer ID in scripts
  • The Procedure Browser is the primary reference for discovering procedure names and argument types
  • file-glob lists files matching a pattern; for-each iterates over the result
  • Scripts saved as .scm in the scripts folder appear in GIMP after a refresh or restart
Common Mistakes
  • Forgetting car around procedure calls — the raw return value is a list, not a usable ID
  • Using backslashes in file paths — Script-Fu requires forward slashes on all platforms
  • Running the batch loop before creating the output directory — Script-Fu cannot create folders
  • Not calling gimp-image-delete at the end of each loop iteration — GIMP accumulates open images in memory and will slow or crash on large batches
  • Using gimp-image-scale without recalculating the height — images scale to unexpected dimensions
  • Calling script-fu-register without a corresponding script-fu-menu-register — the script exists but never appears in a menu
Quick Quiz
  • What does car do, and why is it needed after most GIMP procedure calls?
  • Which Script-Fu function lists all files matching a glob pattern in a directory?
  • What must you call at the end of each batch loop iteration to prevent memory accumulation?
  • Where do you save a .scm file so GIMP loads it automatically at startup?
  • Which GIMP dialog lets you search for the Script-Fu name of any menu operation?
Next Session Preview
  • Session 17 covers Batch Processing in depth — File → Export, naming conventions, and folder pipelines
  • You'll build a full export pipeline: flatten → sharpen → rename → export to multiple formats
  • Session 17 also covers GIMP's built-in Export As dialogs and their format-specific settings
  • Prerequisite: the batch-resize script from this session working correctly on your test folder
📚 Best Resources
  • 01
    GIMP Official Docs Authoritative reference for every tool, dialog, and preference in GIMP 3.x.
    docs.gimp.org ↗
  • 02
    Davies Media Design Video walkthroughs specifically for GIMP 3.x — best free companion to this course.
    YouTube ↗
  • 03
    GIMP Forums Community support for troubleshooting, plug-in questions, and workflow advice.
    gimp-forum.net ↗