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.
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, andcarextracts the first element. Assign the image ID to a variable with(let* ((image (car (gimp-file-load ...))) ...) ...)— thelet*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-fulltakes an image ID, width, height, and interpolation type. To scale proportionally, calculate the new height first or usegimp-image-scalewhich 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) withfor-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-registerandscript-fu-menu-register. Save the file with a.scmextension 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. Thescript-fu-registercall 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), andfile-jpeg-saveorgimp-file-overwrite-pngfor export. Position the watermark usinggimp-layer-set-offsetswith coordinates derived fromgimp-image-widthandgimp-image-heightso 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-eachpattern. Update both the input glob path and the output directory string. Thebasenamefunction 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-fullupscales it — add a conditional check withifto 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.scmin 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 inscript-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-maskto confirm its argument order: it takes run-mode, image, drawable, amount (0.5), radius (0.5), and threshold (4). Call it withRUN-NONINTERACTIVEto 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 usinggimp-image-width,gimp-image-height,gimp-drawable-width, andgimp-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 withgimp-layer-set-opacity. Flatten withgimp-image-flattenbefore 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.
Session 16 Review
- Script-Fu uses prefix notation — function name first, then arguments, all wrapped in parentheses
- Most GIMP procedures return a list; use
carto 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-globlists files matching a pattern;for-eachiterates over the result- Scripts saved as
.scmin the scripts folder appear in GIMP after a refresh or restart
- Forgetting
cararound 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-deleteat the end of each loop iteration — GIMP accumulates open images in memory and will slow or crash on large batches - Using
gimp-image-scalewithout recalculating the height — images scale to unexpected dimensions - Calling
script-fu-registerwithout a correspondingscript-fu-menu-register— the script exists but never appears in a menu
- What does
cardo, 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
.scmfile so GIMP loads it automatically at startup? - Which GIMP dialog lets you search for the Script-Fu name of any menu operation?
- 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
-
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 ↗