Save Layers

Script for Adobe Photoshop

Save each layer of images to a separate file.

  • Process active image or folder of images
  • Choose layer types to include
  • Save with layer groups or without
  • Keep layers transparent or flatten
  • Output JPG, PNG, TIF, or PSD
Download
Save Layers

FREE to download
Please make a contribution

Many scripts are free to download thanks to the support of users. Help me keep developing new scripts by supporting my work. Click the button to make a contribution of any amount. Thank you.

How-to Video

How to use the script

The interface has three sections, Process, Include and Output. Set options as desired and click the OK button to begin. A progress bar is displayed as layers are saved.

Section 1: Process

Active image — processes the image that is currently open and the top-most window if multiple images are open. Each layer of the image is saved to a separate file.

Folder — Processes a folder of images. Click the Folder button to select the desired input folder. If the option Include subfolders is checked, all folders in the selected folder are also processed. If Photoshop encounters non-image files that it cannot open, the file is ignored. For each image file found, the script saves each of its layers to a separate file.

Section 2: Include

Each checkbox, when enabled, saves specific layer types. When disabled, the layer type is ignored.

Pixel — normal image and Smart Object layers.

Adjustment — any adjustment layer, for example, levels, curves, hue/saturation, etc.

Text — unrasterized text layers.

Fill — solid, gradient, and pattern fill layers.

Groups — when enabled, each layer retains the group structure in which it is found. When disabled, groups are ignored and results saved are the each layer only.

Other — 3D and video layers.

Section 3: Output

Output file names are the input file name, an underscore, and the layer name.

Folder — select the location to which layers are output.

Format — select the file format to output. Choices are JPG, PNG, PSD, or TIF.

Flatten — result is a single “Background” layer. JPG always performs this step. When disabled, layers remain as-is.

Quality — applies to JPEG images. Valid range is from 0 to 12. 0 is extreme compression resulting in low quality. 12 is light compression that is virtually indistinguishable from the original, the highest possible quality, which of course, results in the largest file size. 10 to 12 is recommended for print or other high-quality reproduction. For web images, 5 to 8 is an acceptable range.

Embed color profile — applies to JPEG images. Embeds into the JPG the current color profile, either for the original color space if not converted, or the profile selected for the Convert to profile option. The option exists for JPG so that files intended for print can include profiles, important to preserve in that case, but JPG for web may omit profiles, as the profile is excess and only increases file size.

PSD and TIF always embed profiles, and PNG never embeds profiles.

Source code

(download button below)

/*

Save Layers
Copyright 2022 William Campbell
All Rights Reserved
https://www.marspremedia.com/contact

Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

*/

(function () {

    var title = "Save Layers";

    if (!/photoshop/i.test(app.name)) {
        alert("Script for Photoshop", title, false);
        return;
    }

    app.displayDialogs = DialogModes.ERROR;

    // Script variables.
    var count;
    var doneMessage;
    var error;
    var extensions;
    var folderInput;
    var folderOutput;
    var formats;
    var progress;
    var qualities;

    // Reusable UI variables.
    var g; // group
    var p; // panel
    var w; // window

    // Permanent UI variables.
    var btnCancel;
    var btnFolderInput;
    var btnFolderOutput;
    var btnOk;
    var cbAdjust;
    var cbEmbedProfile;
    var cbFill;
    var cbFlatten;
    var cbGroups;
    var cbOther;
    var cbPixel;
    var cbSubfolders;
    var cbText;
    var grpFolderInput;
    var grpJpg;
    var listFormat;
    var listQuality;
    var rbProcessActive;
    var rbProcessFolder;
    var txtFolderInput;
    var txtFolderOutput;

    // LANGUAGE EXTENSIONS

    if (!String.prototype.trim) {
        String.prototype.trim = function () {
            return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "");
        };
    }

    // SETUP

    extensions = [
        "jpg",
        "png",
        "psd",
        "tif"
    ];
    formats = [
        "JPG",
        "PNG",
        "PSD",
        "TIF"
    ];
    qualities = [
        "0",
        "1",
        "2",
        "3",
        "4",
        "5",
        "6",
        "7",
        "8",
        "9",
        "10",
        "11",
        "12"
    ];

    // CREATE PROGRESS WINDOW

    progress = new Window("palette", "Progress");
    progress.t = progress.add("statictext");
    progress.t.preferredSize = [450, -1];
    progress.b = progress.add("progressbar");
    progress.b.preferredSize = [450, -1];
    progress.add("statictext", undefined, "Press ESC to cancel");
    progress.display = function (message) {
        message && (this.t.text = message);
        this.show();
        app.refresh();
    };
    progress.increment = function () {
        this.b.value++;
    };
    progress.set = function (steps) {
        this.b.value = 0;
        this.b.minvalue = 0;
        this.b.maxvalue = steps;
    };

    // CREATE USER INTERFACE

    w = new Window("dialog", title);
    w.alignChildren = "fill";
    // Panel 'Process'
    p = w.add("panel", undefined, "Process");
    p.alignChildren = "left";
    p.margins = [18, 18, 18, 18];
    g = p.add("group");
    rbProcessActive = g.add("radiobutton", undefined, "Active image");
    rbProcessFolder = g.add("radiobutton", undefined, "Folder");
    cbSubfolders = g.add("checkbox", undefined, "Include subfolders");
    grpFolderInput = p.add("group");
    btnFolderInput = grpFolderInput.add("button", undefined, "Folder...");
    txtFolderInput = grpFolderInput.add("statictext", undefined, undefined, {
        truncate: "middle"
    });
    txtFolderInput.preferredSize = [300, -1];
    // Panel 'Include'
    p = w.add("panel", undefined, "Include");
    p.alignChildren = "left";
    p.orientation = "row";
    p.margins = [18, 18, 18, 18];
    p.spacing = 48;
    g = p.add("group");
    g.alignChildren = "left";
    g.orientation = "column";
    cbPixel = g.add("checkbox", undefined, "Pixel");
    cbAdjust = g.add("checkbox", undefined, "Adjustment");
    g = p.add("group");
    g.alignChildren = "left";
    g.orientation = "column";
    cbText = g.add("checkbox", undefined, "Text");
    cbFill = g.add("checkbox", undefined, "Fill");
    g = p.add("group");
    g.alignChildren = "left";
    g.orientation = "column";
    cbGroups = g.add("checkbox", undefined, "Groups");
    cbOther = g.add("checkbox", undefined, "Other");
    // Panel 'Output'
    p = w.add("panel", undefined, "Output");
    p.alignChildren = "left";
    p.margins = [18, 18, 18, 18];
    g = p.add("group");
    btnFolderOutput = g.add("button", undefined, "Folder...");
    txtFolderOutput = g.add("statictext", undefined, "", {
        truncate: "middle"
    });
    txtFolderOutput.preferredSize = [300, -1];
    g = p.add("group");
    g.add("statictext", undefined, "Format:");
    listFormat = g.add("dropdownlist", undefined, formats);
    g = p.add("group");
    cbFlatten = g.add("checkbox", undefined, "Flatten");
    grpJpg = g.add("group");
    grpJpg.add("statictext", undefined, "Quality:");
    listQuality = grpJpg.add("dropdownlist", undefined, qualities);
    cbEmbedProfile = grpJpg.add("checkbox", undefined, "Embed profile");
    // Action Buttons
    g = w.add("group");
    g.alignment = "center";
    btnOk = g.add("button", undefined, "OK");
    btnCancel = g.add("button", undefined, "Cancel");
    // Panel Copyright
    p = w.add("panel");
    p.add("statictext", undefined, "Copyright 2022 William Campbell");

    // SET DEFAULT VALUES
    // PRECEDE UI EVENT HANDLERS SO THEY DON'T FIRE WHILE SETTING VALUES.

    if (app.documents.length) {
        rbProcessActive.value = true;
    } else {
        rbProcessActive.value = false;
        rbProcessActive.enabled = false;
        rbProcessFolder.value = true;
    }
    cbPixel.value = true;
    cbText.value = true;
    cbGroups.value = true;
    cbAdjust.value = true;
    cbFill.value = true;
    cbOther.value = true;
    txtFolderOutput.text = "";
    listFormat.selection = 2; // 0=JPG, 1=PNG, 2=PSD, 3=TIF
    cbFlatten.value = false;
    listQuality.selection = 10;
    cbEmbedProfile.value = false;
    configureUi();

    // UI ELEMENT EVENT HANDLERS

    // Panel 'Process'
    rbProcessActive.onClick = configureUi;
    rbProcessFolder.onClick = configureUi;
    btnFolderInput.onClick = function () {
        var f = Folder.selectDialog("Select input folder");
        if (f) {
            txtFolderInput.text = Folder.decode(f.fsName);
        }
        rbProcessActive.value = false;
        configureUi();
    };
    // Panel 'Output'
    btnFolderOutput.onClick = function () {
        var f = Folder.selectDialog("Select output folder", txtFolderOutput.text);
        if (f) {
            txtFolderOutput.text = Folder.decode(f.fsName);
        }
    };
    listFormat.onChange = configureUi;
    // Action Buttons
    btnOk.onClick = function () {
        if (rbProcessFolder.value && !new Folder(txtFolderInput.text).exists) {
            alert("Select input folder", " ", false);
            return;
        }
        if (!new Folder(txtFolderOutput.text).exists) {
            txtFolderOutput.text = "";
            alert("Select output folder", " ", false);
            return;
        }
        w.close(1);
    };
    btnCancel.onClick = function () {
        w.close(0);
    };

    // DISPLAY THE DIALOG

    if (w.show() == 1) {
        doneMessage = "";
        try {
            process();
            if (rbProcessActive.value) {
                doneMessage = "Done";
            } else {
                doneMessage = doneMessage || count + " files processed";
            }
        } catch (e) {
            if (/User cancel/.test(e.message)) {
                doneMessage = "Canceled";
            } else {
                error = error || e;
                doneMessage = "An error has occurred.\nLine " + error.line + ": " + error.message;
            }
        }
        app.bringToFront();
        progress.close();
        doneMessage && alert(doneMessage, title, error);
    }

    //====================================================================
    //               END PROGRAM EXECUTION, BEGIN FUNCTIONS
    //====================================================================

    function addDoc(d) {
        // Creates a new document.
        // Parameter 'd' is document object.
        // If defined, new doc is based on doc object passed.
        // Otherwise call app.documents.add() no parameters.
        var newDoc;
        var newDocumentMode;
        if (d) {
            switch (d.mode) {
                case DocumentMode.RGB:
                    newDocumentMode = NewDocumentMode.RGB;
                    break;
                case DocumentMode.CMYK:
                    newDocumentMode = NewDocumentMode.CMYK;
                    break;
                case DocumentMode.GRAYSCALE:
                    newDocumentMode = NewDocumentMode.GRAYSCALE;
                    break;
                case DocumentMode.LAB:
                    newDocumentMode = NewDocumentMode.LAB;
                    break;
                case DocumentMode.BITMAP:
                    newDocumentMode = NewDocumentMode.BITMAP;
                    break;
            }
            newDoc = app.documents.add(
                d.width,
                d.height,
                d.resolution,
                d.name.replace(/\.[^\.]*$/, ""),
                newDocumentMode,
                DocumentFill.WHITE,
                d.pixelAspectRatio,
                d.bitsPerChannel
            );
            if (d.colorProfileType == ColorProfile.NONE) {
                newDoc.colorProfileType = ColorProfile.NONE;
            } else {
                newDoc.colorProfileName = d.colorProfileName;
            }
        } else {
            newDoc = app.documents.add();
        }
        return newDoc;
    }

    function configureUi() {
        if (!(rbProcessActive.value || rbProcessFolder.value)) {
            rbProcessFolder.value = true;
        }
        cbSubfolders.enabled = rbProcessFolder.value;
        grpFolderInput.enabled = rbProcessFolder.value;
        if (listFormat.selection && listFormat.selection.index == 0) {
            // JPG
            grpJpg.visible = true;
            cbFlatten.value = true;
            cbFlatten.enabled = false;
        } else {
            grpJpg.visible = false;
            cbFlatten.value = false;
            cbFlatten.enabled = true;
        }
    }

    function getFiles(folder, subfolders) {
        // folder = folder object, not folder name.
        // subfolders = true to include subfolders.
        // Ignores hidden files and folders.
        var f;
        var files;
        var i;
        var pattern;
        var results = [];
        pattern = new RegExp(
            "\.8PBS|AFX|AI|ARW|BLZ|BMP|CAL|CALS|CIN|CR2|CRW|CT|DCM|DCR|DCS|DCX|DDS|" +
            "DIB|DIC|DNG|DPX|EPS|EPSF|EXR|FFF|FIF|GIF|HDP|HDR|HEIC|HEIF|ICB|ICN|ICO|" +
            "ICON|IIQ|IMG|J2C|J2K|JIF|JIFF|JP2|JPC|JPE|JPEG|JPF|JPG|JPS|JPX|JXR|KDK|" +
            "KMZ|KODAK|MOS|MRW|NCR|NEF|ORF|PAT|PBM|PCT|PCX|PDD|PDF|PDP|PEF|PGM|PICT|" +
            "PMG|PNG|PPM|PS|PSB|PSD|PSDC|PSID|PVR|PXR|RAF|RAW|RLE|RSR|SCT|SRF|TGA|TIF|" +
            "TIFF|TRIF|U3D|VDA|VST|WBMP|WDP|WMP|X3F$", "i");
        files = folder.getFiles();
        for (i = 0; i < files.length; i++) {
            f = files[i];
            if (!f.hidden) {
                if (f instanceof Folder && subfolders) {
                    // Recursive (function calls itself).
                    results = results.concat(getFiles(f, subfolders));
                } else if (f instanceof File && pattern.test(f.name)) {
                    results.push(f);
                }
            }
        }
        return results;
    }

    function getLayers() {
        var kind;
        var layers = [];
        var searchLayers = function (o) {
            for (var i = 0; i < o.layers.length; i++) {
                if (o.layers[i].typename == "LayerSet") {
                    layers.concat(searchLayers(o.layers[i]));
                } else {
                    kind = o.layers[i].kind;
                    if (
                        kind == LayerKind.NORMAL ||
                        kind == LayerKind.SMARTOBJECT
                    ) {
                        if (!cbPixel.value) continue;
                    } else if (
                        kind == LayerKind.TEXT
                    ) {
                        if (!cbText.value) continue;
                    } else if (
                        kind == LayerKind.SOLIDFILL ||
                        kind == LayerKind.GRADIENTFILL ||
                        kind == LayerKind.PATTERNFILL
                    ) {
                        if (!cbFill.value) continue;
                    } else if (
                        kind == LayerKind.LEVELS ||
                        kind == LayerKind.CURVES ||
                        kind == LayerKind.COLORBALANCE ||
                        kind == LayerKind.BRIGHTNESSCONTRAST ||
                        kind == LayerKind.HUESATURATION ||
                        kind == LayerKind.SELECTIVECOLOR ||
                        kind == LayerKind.CHANNELMIXER ||
                        kind == LayerKind.GRADIENTMAP ||
                        kind == LayerKind.INVERSION ||
                        kind == LayerKind.THRESHOLD ||
                        kind == LayerKind.POSTERIZE ||
                        kind == LayerKind.PHOTOFILTER ||
                        kind == LayerKind.EXPOSURE ||
                        kind == LayerKind.BLACKANDWHITE ||
                        kind == LayerKind.VIBRANCE ||
                        kind == LayerKind.COLORLOOKUP
                    ) {
                        if (!cbAdjust.value) continue;
                    } else if (
                        kind == LayerKind.LAYER3D ||
                        kind == LayerKind.VIDEO
                    ) {
                        if (!cbOther.value) continue;
                    }
                    layers.push(o.layers[i]);
                }
            }
        };
        searchLayers(app.activeDocument);
        return layers;
    }

    function process() {
        var doc;
        var file;
        var fileName;
        var files;
        var i;
        app.displayDialogs = DialogModes.NO;
        folderInput = new Folder(txtFolderInput.text);
        folderOutput = new Folder(txtFolderOutput.text);
        progress.display("Initializing...");
        try {
            if (rbProcessActive.value) {
                // Process Active image.
                doc = app.activeDocument;
                processDoc(doc);
            } else {
                // Process Folder.
                progress.display("Reading folder...");
                files = getFiles(folderInput, cbSubfolders.value);
                if (!files.length) {
                    doneMessage = "No files found in selected folder";
                    return;
                }
                progress.set(files.length);
                count = 0;
                for (i = 0; i < files.length; i++) {
                    doc = null;
                    file = files[i];
                    fileName = File.decode(file.name);
                    progress.display(fileName);
                    try {
                        doc = app.open(file);
                        processDoc(doc);
                    } catch (e) {
                        if (/open options are incorrect/.test(e.message)) {
                            // Photoshop doesn't know this kind of file; ignore.
                        } else {
                            throw e;
                        }
                    } finally {
                        doc.close(SaveOptions.DONOTSAVECHANGES);
                    }
                    count++;
                    progress.increment();
                }
            }
        } finally {
            app.displayDialogs = DialogModes.ERROR;
        }
    }

    function processDoc(doc) {
        var baseName;
        var baseNameDoc;
        var docDupe;
        var docLayer;
        var i;
        var layer;
        var layerName;
        var layers;
        baseNameDoc = doc.name.replace(/\.[^\.]*$/, "");
        layers = getLayers();
        if (rbProcessActive.value) {
            progress.set(layers.length);
        }
        for (i = 0; i < layers.length; i++) {
            layer = layers[i];
            layerName = layer.name;
            baseName = baseNameDoc + "_" + layerName;
            // Replace invalid characters.
            baseName = baseName.replace(/[\\\/:"*?<>|]/g, "_");
            // Remove low non-printing ascii.
            baseName = baseName.replace(/[\x00-\x1F]/g, "");
            // Remove leading and trailing whitespace.
            baseName = baseName.trim();
            progress.display(baseName);
            docLayer = addDoc(doc);
            app.activeDocument = doc;
            if (cbGroups.value) {
                docDupe = doc.duplicate(baseName);
                try {
                    app.activeDocument = docDupe;
                    removeLayers(layer.id);
                    removeEmptyLayerSets();
                    docDupe.layers[0].duplicate(docLayer);
                } finally {
                    docDupe.close(SaveOptions.DONOTSAVECHANGES);
                }
            } else {
                layer.duplicate(docLayer);
            }
            app.activeDocument = docLayer;
            docLayer.layers[1].remove();
            docLayer.layers[0].name = layerName;
            // Flatten.
            if (cbFlatten.value) {
                docLayer.flatten();
            }
            // Save.
            saveAndClose(docLayer, baseName);
            if (rbProcessActive.value) {
                progress.increment();
            }
        }
    }

    function removeEmptyLayerSets() {
        var searchLayerSets = function (o) {
            for (var i = o.layerSets.length - 1; i > -1; i--) {
                searchLayerSets(o.layerSets[i]);
                if (o.layerSets[i].layers.length == 0) {
                    o.layerSets[i].remove();
                }
            }
        };
        searchLayerSets(app.activeDocument);
    }

    function removeLayers(keepId) {
        var searchLayers = function (o) {
            for (var i = o.layers.length - 1; i > -1; i--) {
                o.layers[i].visible = true;
                if (o.layers[i].typename == "LayerSet") {
                    searchLayers(o.layers[i]);
                } else {
                    if (o.layers[i].id == keepId) {
                        continue;
                    }
                    o.layers[i].remove();
                }
            }
        };
        searchLayers(app.activeDocument);
    }

    function saveAndClose(doc, baseName) {
        var embedColorProfile;
        var extension;
        var format;
        var fullPath;
        var outputFile;
        var outputName;
        var saveOptions;
        embedColorProfile = doc.colorProfileType != ColorProfile.NONE;
        format = listFormat.selection.index;
        extension = extensions[format];
        // Add extension and build full path.
        outputName = baseName + "." + extension;
        fullPath = txtFolderOutput.text + "/" + outputName;
        outputFile = new File(fullPath);
        switch (format) {
            case 0: // JPG
                saveOptions = new JPEGSaveOptions();
                saveOptions.embedColorProfile = embedColorProfile && cbEmbedProfile.value;
                saveOptions.formatOptions = FormatOptions.STANDARDBASELINE;
                saveOptions.matte = MatteType.WHITE;
                saveOptions.quality = Number(listQuality.selection.text);
                saveOptions.scans = 3;
                break;
            case 1: // PNG
                // PNG always untagged 8-bit sRGB.
                doc.bitsPerChannel = BitsPerChannelType.EIGHT;
                doc.convertProfile("sRGB IEC61966-2.1", Intent.RELATIVECOLORIMETRIC);
                doc.colorProfileType = ColorProfile.NONE;
                saveOptions = new PNGSaveOptions();
                saveOptions.compression = 5;
                saveOptions.interlaced = false;
                break;
            case 2: // PSD
                saveOptions = new PhotoshopSaveOptions();
                saveOptions.alphaChannels = true;
                saveOptions.annotations = true;
                saveOptions.embedColorProfile = embedColorProfile;
                saveOptions.layers = !cbFlatten.value;
                saveOptions.spotColors = true;
                break;
            case 3: // TIF
                saveOptions = new TiffSaveOptions();
                saveOptions.alphaChannels = true;
                saveOptions.annotations = true;
                saveOptions.embedColorProfile = embedColorProfile;
                saveOptions.imageCompression = TIFFEncoding.TIFFLZW;
                saveOptions.layerCompression = LayerCompression.ZIP;
                saveOptions.layers = !cbFlatten.value;
                saveOptions.spotColors = true;
                break;
            default:
                // None of the above.
                throw new Error("Bad file format.");
        }
        // Ensure output folder exists.
        if (!folderOutput.exists) {
            folderOutput.create();
        }
        // Save.
        doc.saveAs(outputFile, saveOptions);
        doc.close(SaveOptions.DONOTSAVECHANGES);
    }

})();
Many scripts are free to download thanks to the support of users. Help me keep developing new scripts by supporting my work. Click the button to make a contribution of any amount. Thank you.
Download
Save Layers

For help installing scripts, see How to Install and Use Scripts in Adobe Creative Cloud Applications.

Also available for hire to program custom solutions. Contact William for more information.

IMPORTANT: by downloading any of the scripts on this page you agree that the software is provided without any warranty, express or implied. USE AT YOUR OWN RISK. Always make backups of important data.