Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
9

Exporting artboards to webp format?

Community Beginner ,
May 11, 2022 May 11, 2022

Great that the latest Photoshop has "full" webp support, but lacking is the ability to export artboards to webp format. I build a lot of my website graphics as artboards, but it looks like I will need an intermediary step of saving the artboards as PSD files first, and then exporting them as webp? Or am I missing something?

 

Thanks,

 

Judi

TOPICS
Windows
24.2K
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 2 Correct answers

Community Beginner , Jun 12, 2022 Jun 12, 2022

@Stephen Marsh Solved! Your script works perfectly. The problem was that I had the "Open As Layer" event running in the Script Events Manager. Turning that off solved the errors I was getting, plus a whole heap of other stuff relating to exporting artboards. I rely hugely on Open As Layer to help document my compositing workflow, but hadn't connected the dots to its impact on other scripts. But it's easy to toggle the event on and off as needed. Thank you so much for all your help on this, and f

...
Translate
Community Expert , Jul 26, 2025 Jul 26, 2025

Here is the 2.1 script version:

 

Artboards-to-WEBP-PS2025-scriptUI-GUI-v2-1.png

 

* Batch Output to WebP - Automatically saves all your artboards to WebP format in one go, saving you from the tedious process of saving each artboard individually

 

* Dual Format Support - Can export both WebP and JPEG versions simultaneously, so you get modern WebP files for performance plus JPEG fallbacks for compatibility

 

* Flexible Compression Options - Choose between lossy and lossless WebP compression with a quality slider (0-100), giving you control

...
Translate
Adobe
Community Expert ,
Jun 12, 2022 Jun 12, 2022

Thank you for the feedback, they all should have produced the same result.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
May 31, 2023 May 31, 2023

Hi Stephen_A_Marsh,
This is awesome! Really helpful, thank you. 
One question I had was around the filenaming and wondered if it was possible to add that functionality into this script. 

Usually, when we export Artboards to Files, there is an additional pop up (attached) which allows you to add a prefix to the filename, rather than taking the Artboard name. I wondered if it's possible to add that pop up on this export functionality at all? It's not critical but would help the process of filenaming and organisation. 

I checked the Artboards to Files jsx file and could see part of the functionality but I'm not saavy enough to work out how to add that to your awesome script. 

Really appreciate your work on this! 
Thanks so much!

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
May 31, 2023 May 31, 2023

@Mel30188944k1h7 

 

Yes, this is possible. Please provide a cropped image of the layers panel with all artboards visible (they can be collapsed) and the final output names, making it clear which is the doc name and which is the added prefix or suffix.

 

The doc name will be static and the prefix/suffix will also be static, so files would overwrite each other without a sequential number.

 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Jun 01, 2023 Jun 01, 2023

Hi @Stephen Marsh 
Awesome, thank you. I've included 3 files to show the process of how I export currently Artboards to Files with the prefix. 

01 - This shows the Artboards and the layers as you requested. The artboards are called, Landscape, Portrait and Sqaure. 
02 - This shows the pop up where I specify a prefix for the file name. 
03 - This shows the output of the files in Explorer but in PNG format
03 - (3 images) - Correct output filenames but PNG

I tried to upload the webp images or a zip here as well as the output from your script but it wont let me as it does not support the fileformat.

 

What I need is the output of 03 - but as webP files. 

I hope that makes sense! Thank you so much for the support!  

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 02, 2023 Jun 02, 2023

@Mel30188944k1h7 â€“ Thanks, I think that it makes sense! You have unique artboard names + a static prefix with an underscore separator. Thank you for clarifying. I should be able to modify the previous script easily.

 

Note: I have been getting failures on the final artboard, which is converted to a smart object! Here is the updated 1.5 code:

 

/*
Artboards-to-WEBP-PS2022.jsx
14th May 2022, v1.5 - Stephen Marsh

* Only for Photoshop 2022 (v23.x.x) or later!
* Saves artboards as lossy WebP - Quality: 100
* Prompts for a save location and prompts for a prefix to be added to the artboard name
* Existing files will be silently overwritten

Instructions for saving and installing:
https://prepression.blogspot.com/2017/11/downloading-and-installing-adobe-scripts.html
*/

// Original script author below:

// =========================================================================
// artboardsToPNG.jsx - Adobe Photoshop Script
// Version: 0.6.0
// Requirements: Adobe Photoshop CC 2015, or higher
// Author: Anton Lyubushkin (nvkz.nemo@gmail.com)
// Website: http://lyubushkin.pro/
// =========================================================================

#target photoshop

app.bringToFront();

// Ensure that version 2022 or later is being used
var versionNumber = app.version.split(".");
var versionCheck = parseInt(versionNumber);
if (versionCheck < 23) {
    alert("You must use Photoshop 2022 or later to save using native WebP format...");

} else {
    if (app.documents.length !== 0) {

        var docRef = app.activeDocument,
            allArtboards,
            artboardsCount = 0,
            inputFolder = Folder.selectDialog("Select an output folder to save the artboards as WebP:");
            var preFix = prompt("Add the prefix including separator character to the artboard name:");


        if (inputFolder) {
            function getAllArtboards() {
                try {
                    var ab = [];
                    var theRef = new ActionReference();
                    theRef.putProperty(charIDToTypeID('Prpr'), stringIDToTypeID("artboards"));
                    theRef.putEnumerated(charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));
                    var getDescriptor = new ActionDescriptor();
                    getDescriptor.putReference(stringIDToTypeID("null"), theRef);
                    var abDesc = executeAction(charIDToTypeID("getd"), getDescriptor, DialogModes.NO).getObjectValue(stringIDToTypeID("artboards"));
                    var abCount = abDesc.getList(stringIDToTypeID('list')).count;
                    if (abCount > 0) {
                        for (var i = 0; i < abCount; ++i) {
                            var abObj = abDesc.getList(stringIDToTypeID('list')).getObjectValue(i);
                            var abTopIndex = abObj.getInteger(stringIDToTypeID("top"));
                            ab.push(abTopIndex);

                        }
                    }
                    return [abCount, ab];
                } catch (e) {
                    alert(e.line + '\n' + e.message);
                }
            }

            function selectLayerByIndex(index, add) {
                add = undefined ? add = false : add
                var ref = new ActionReference();
                ref.putIndex(charIDToTypeID("Lyr "), index + 1);
                var desc = new ActionDescriptor();
                desc.putReference(charIDToTypeID("null"), ref);
                if (add) desc.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("selectionModifierType"), stringIDToTypeID("addToSelection"));
                desc.putBoolean(charIDToTypeID("MkVs"), false);
                executeAction(charIDToTypeID("slct"), desc, DialogModes.NO);
            }

            function ungroupLayers() {
                app.runMenuItem(stringIDToTypeID('ungroupLayersEvent'));
            }

            function crop() {
                var desc1 = new ActionDescriptor();
                desc1.putBoolean(charIDToTypeID('Dlt '), true);
                executeAction(charIDToTypeID('Crop'), desc1, DialogModes.NO);
            }

            function saveAsWebP(_name) {
                var s2t = function (s) {
                    return app.stringIDToTypeID(s);
                };
                var descriptor = new ActionDescriptor();
                var descriptor2 = new ActionDescriptor();
                descriptor2.putEnumerated(s2t("compression"), s2t("WebPCompression"), s2t("compressionLossy"));
                descriptor2.putInteger(s2t("quality"), 100);
                descriptor2.putBoolean(s2t("includeXMPData"), true);
                descriptor2.putBoolean(s2t("includeEXIFData"), true);
                descriptor2.putBoolean(s2t("includePsExtras"), true);
                descriptor.putObject(s2t("as"), s2t("WebPFormat"), descriptor2);
                descriptor.putPath(s2t("in"), new File(inputFolder + '/' + _name + '.webp'), true);
                descriptor.putBoolean(s2t("lowerCase"), true);
                descriptor.putEnumerated(s2t("saveStage"), s2t("saveStageType"), s2t("saveSucceeded"));
                executeAction(s2t("save"), descriptor, DialogModes.NO);
            }

            function main(i) {
                selectLayerByIndex(allArtboards[1][i]);
                // RegEx replace illegal filename characters with a hyphen
                var artboardName = preFix + app.activeDocument.activeLayer.name.replace(/[:\/\\*\?\"\<\>\|\\\r\\\n.]/g, "_"); // "/\:*?"<>|\r\n" -> "-"

                executeAction(stringIDToTypeID("newPlacedLayer"), undefined, DialogModes.NO);
                executeAction(stringIDToTypeID("placedLayerEditContents"), undefined, DialogModes.NO);
                app.activeDocument.selection.selectAll();
                try {
                    ungroupLayers();
                } catch (e) {
                    alert("There was an unexpected error with the ungroupLayers function!")
                    app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
                }
                crop();
                saveAsWebP(artboardName);
                app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
            }

            allArtboards = getAllArtboards();

            artboardsCount = allArtboards[0];

            for (var i = 0; i < artboardsCount; i++) {
                docRef.suspendHistory('Artboards to WebP', 'main(' + i + ')');
                app.refresh();
                app.activeDocument.activeHistoryState = app.activeDocument.historyStates[app.activeDocument.historyStates.length - 2];
            }
        }

        app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
        alert('WebP files saved to: ' + '\r' + inputFolder.fsName);
        // inputFolder.execute();

    } else {
        alert('You must have a document open!');
    }
}

 

Adobe really does need to update the default scripts that ship with Photoshop.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Jun 06, 2023 Jun 06, 2023

Hey @Stephen Marsh,
This is amazing, thank you so much for your help! Let me know if I can upvote / rate this script anywhere else for you! Works like a dream. Really appreciate your time and support to make our lives easier 🙂 
Have a wonderful day! 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 06, 2023 Jun 06, 2023

You're welcome!

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Jun 28, 2023 Jun 28, 2023

Hello,

 

Your script is nice, but it happens, many times, if the layout is more complex, to get a very big WEBP file.

It happend to be between the range of 5mb to 150 mb.
The size in pixels for the artboard i try is small, like 600x400px.
If i save the same file "manualy" i get a WEBP of  under 100kb.

You have any ideea why?

 

Also if the PSD file is CMYK instead of RGB the script gives an error and stops.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 28, 2023 Jun 28, 2023

 

@alexandrum2701 

 

I forgot to reply/update, here is a new 1.6 version that converts to sRGB if the mode isn't RGB. Now updated to v1.8:

 

/*
Artboards-to-WEBP-PS2022.jsx
18th June 2024, v1.8 - Stephen Marsh
https://community.adobe.com/t5/photoshop-ecosystem-discussions/exporting-artboards-to-webp-format/td-p/12937135

* Only for Photoshop 2022 (v23.x.x) or later!
* Saves artboards as lossy WebP - Quality: 100
* Prompts for a save location and prompts for a prefix to be added to the artboard name (uses the doc name by default)
* Existing files will be silently overwritten
* Converts non-RGB to sRGB color space, RGB color mode and 8 bpc
* If RGB the original color space is retained, beware if using ProPhoto RGB!
* Added a "script running" window to provide feedback during the script execution
* Creates a temporary working document

Instructions for saving and installing:
https://prepression.blogspot.com/2017/11/downloading-and-installing-adobe-scripts.html
*/

// Original script author below:

// =========================================================================
// artboardsToPNG.jsx - Adobe Photoshop Script
// Version: 0.6.0
// Requirements: Adobe Photoshop CC 2015, or higher
// Author: Anton Lyubushkin (nvkz.nemo@gmail.com)
// Website: http://lyubushkin.pro/
// =========================================================================

#target photoshop

    (function () {

        app.bringToFront();

        // Ensure that version 2022 or later is being used
        var versionNumber = app.version.split(".");
        var versionCheck = parseInt(versionNumber);
        if (versionCheck < 23) {
            alert("You must use Photoshop 2022 or later to save using native WebP format...");

        } else {
            if (app.documents.length !== 0) {

                var docRef = app.activeDocument,
                    allArtboards,
                    artboardsCount = 0;

                var outputFolder = Folder.selectDialog("Select an output folder to save the artboards as WebP:");
                // Test if Cancel button returns null, then do nothing
                if (outputFolder === null) {
                    app.beep();
                    return;
                }

                var preFix = prompt("Add the prefix including separator character to the artboard name:", docRef.name.replace(/\.[^\.]+$/, '') + "_");
                // Test if Cancel button returns null, then do nothing
                if (preFix === null) {
                    app.beep();
                    return;
                }

                // Hide the Photoshop panels
                app.togglePalettes();

                if (outputFolder) {

                    // Dupe to a temp doc
                    app.activeDocument.duplicate(app.activeDocument.name.replace(/\.[^\.]+$/, ''), false);

                    // Clean out unwanted metadata
                    deleteDocumentAncestorsMetadata();
                    removeCRSmeta();

                    // If the doc isn't in RGB mode
                    if (activeDocument.mode !== DocumentMode.RGB)
                        // Convert to sRGB
                        activeDocument.convertProfile("sRGB IEC61966-2.1", Intent.RELATIVECOLORIMETRIC, true, false);
                    // Ensure that the doc mode is RGB (to correctly handle Indexed Color mode)
                    activeDocument.changeMode(ChangeMode.RGB);
                    activeDocument.bitsPerChannel = BitsPerChannelType.EIGHT;

                    allArtboards = getAllArtboards();
                    artboardsCount = allArtboards[0];

                    // Loop over the artboards and run the main function
                    for (var i = 0; i < artboardsCount; i++) {

                        // Script running notification window - courtesy of William Campbell
                        // https://www.marspremedia.com/download?asset=adobe-script-tutorial-11.zip
                        // https://youtu.be/JXPeLi6uPv4?si=Qx0OVNLAOzDrYPB4
                        var working;
                        working = new Window("palette");
                        working.preferredSize = [300, 80];
                        working.add("statictext");
                        working.t = working.add("statictext");
                        working.add("statictext");
                        working.display = function (message) {
                            this.t.text = message || "Script running, please wait...";
                            this.show();
                            app.refresh();
                        };
                        working.display();
                        // Call the main function
                        docRef.suspendHistory('Artboards to WebP', 'main(' + i + ')');
                        app.refresh();
                        app.activeDocument.activeHistoryState = app.activeDocument.historyStates[app.activeDocument.historyStates.length - 2];

                        // Ensure Photoshop has focus before closing the running script notification window
                        app.bringToFront();
                        working.close();
                    }
                }

                app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);

                // Restore the Photoshop panels
                app.togglePalettes();

                // End of script notification
                app.beep();
                alert(artboardsCount + ' WebP files saved to: ' + '\r' + outputFolder.fsName);

                // Open the output folder in Windows Explorer or the Mac Finder
                // outputFolder.execute();


                ///// FUNCTIONS /////

                function getAllArtboards() {
                    try {
                        var ab = [];
                        var theRef = new ActionReference();
                        theRef.putProperty(charIDToTypeID('Prpr'), stringIDToTypeID("artboards"));
                        theRef.putEnumerated(charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));
                        var getDescriptor = new ActionDescriptor();
                        getDescriptor.putReference(stringIDToTypeID("null"), theRef);
                        var abDesc = executeAction(charIDToTypeID("getd"), getDescriptor, DialogModes.NO).getObjectValue(stringIDToTypeID("artboards"));
                        var abCount = abDesc.getList(stringIDToTypeID('list')).count;
                        if (abCount > 0) {
                            for (var i = 0; i < abCount; ++i) {
                                var abObj = abDesc.getList(stringIDToTypeID('list')).getObjectValue(i);
                                var abTopIndex = abObj.getInteger(stringIDToTypeID("top"));
                                ab.push(abTopIndex);

                            }
                        }
                        return [abCount, ab];
                    } catch (e) {
                        alert(e.line + '\n' + e.message);
                    }
                }

                function selectLayerByIndex(index, add) {
                    add = undefined ? add = false : add;
                    var ref = new ActionReference();
                    ref.putIndex(charIDToTypeID("Lyr "), index + 1);
                    var desc = new ActionDescriptor();
                    desc.putReference(charIDToTypeID("null"), ref);
                    if (add) desc.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("selectionModifierType"), stringIDToTypeID("addToSelection"));
                    desc.putBoolean(charIDToTypeID("MkVs"), false);
                    executeAction(charIDToTypeID("slct"), desc, DialogModes.NO);
                }

                function ungroupLayers() {
                    app.runMenuItem(stringIDToTypeID('ungroupLayersEvent'));
                }

                function crop() {
                    var desc1 = new ActionDescriptor();
                    desc1.putBoolean(charIDToTypeID('Dlt '), true);
                    executeAction(charIDToTypeID('Crop'), desc1, DialogModes.NO);
                }

                function saveAsWebP(_name) {
                    var s2t = function (s) {
                        return app.stringIDToTypeID(s);
                    };
                    var descriptor = new ActionDescriptor();
                    var descriptor2 = new ActionDescriptor();
                    descriptor2.putEnumerated(s2t("compression"), s2t("WebPCompression"), s2t("compressionLossy"));
                    descriptor2.putInteger(s2t("quality"), 100);
                    descriptor2.putBoolean(s2t("includeXMPData"), true);
                    descriptor2.putBoolean(s2t("includeEXIFData"), true);
                    descriptor2.putBoolean(s2t("includePsExtras"), true);
                    descriptor.putObject(s2t("as"), s2t("WebPFormat"), descriptor2);
                    descriptor.putPath(s2t("in"), new File(outputFolder + '/' + _name + '.webp'), true);
                    descriptor.putBoolean(s2t("lowerCase"), true);
                    descriptor.putEnumerated(s2t("saveStage"), s2t("saveStageType"), s2t("saveSucceeded"));
                    executeAction(s2t("save"), descriptor, DialogModes.NO);
                }

                // The main working function
                function main(i) {

                    selectLayerByIndex(allArtboards[1][i]);
                    // RegEx replace illegal filename characters with a hyphen
                    var artboardName = preFix + app.activeDocument.activeLayer.name.replace(/[:\/\\*\?\"\<\>\|\\\r\\\n.]/g, "_"); // "/\:*?"<>|\r\n" -> "-"

                    executeAction(stringIDToTypeID("newPlacedLayer"), undefined, DialogModes.NO);
                    executeAction(stringIDToTypeID("placedLayerEditContents"), undefined, DialogModes.NO);
                    app.activeDocument.selection.selectAll();
                    try {
                        ungroupLayers();
                    } catch (e) {
                        alert("There was an unexpected error with the ungroupLayers function!");
                        app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
                    }
                    crop();
                    saveAsWebP(artboardName);
                    app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
                }

                function removeCRSmeta() {
                    //community.adobe.com/t5/photoshop/remove-crs-metadata/td-p/10306935
                    if (!documents.length) return;
                    if (ExternalObject.AdobeXMPScript === undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
                    var xmp = new XMPMeta(app.activeDocument.xmpMetadata.rawData);
                    XMPUtils.removeProperties(xmp, XMPConst.NS_CAMERA_RAW, "", XMPConst.REMOVE_ALL_PROPERTIES);
                    app.activeDocument.xmpMetadata.rawData = xmp.serialize();
                }

                function deleteDocumentAncestorsMetadata() {
                    whatApp = String(app.name); //String version of the app name
                    if (whatApp.search("Photoshop") > 0) { //Check for photoshop specifically, or this will cause errors
                        if (ExternalObject.AdobeXMPScript === undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
                        var xmp = new XMPMeta(activeDocument.xmpMetadata.rawData);
                        // Begone foul Document Ancestors!
                        xmp.deleteProperty(XMPConst.NS_PHOTOSHOP, "DocumentAncestors");
                        app.activeDocument.xmpMetadata.rawData = xmp.serialize();
                    }
                }

            } else {
                alert('You must have a document open!');
            }
        }

    })();

 

As far as file size, in my tests, I get the same size either manually or from the script if the same quality and metadata settings are used.

 

Please provide sample files of the larger files and the smaller files for the same settings and content. Perhaps there is other metadata or something else going on.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Jun 28, 2023 Jun 28, 2023

Hi @Stephen Marsh 

Hope all is well with you! It's me again 🙂 
The script has been working like a dream and it works super well. 
One issue we've encountered with our workflow is that OG images don't support webp in all platforms and this is causing us a bit of an issue, so in fact it seems we need both WebP formats and Jpeg formats. 

Ideally we could export both from the same script so it minimises our workflow. 
Is there someway the script could be adapted to also output JPG format too? Or is this a bit much? 

Worth an ask at least. 
 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 05, 2024 Jun 05, 2024
quote

Hi @Stephen Marsh 

Hope all is well with you! It's me again 🙂 
The script has been working like a dream and it works super well. 
One issue we've encountered with our workflow is that OG images don't support webp in all platforms and this is causing us a bit of an issue, so in fact it seems we need both WebP formats and Jpeg formats. 

Ideally we could export both from the same script so it minimises our workflow. 
Is there someway the script could be adapted to also output JPG format too? Or is this a bit much? 

Worth an ask at least. 
 


By @Mel30188944k1h7

 

I just stumbled over this post, here is a new version of the script that saves both JPEG and WebP versions:

 

/*
Artboards-to-JPEG-and-WEBP-PS2022.jsx
18th June 2024, v1.3 - Stephen Marsh

* Only for Photoshop 2022 (v23.x.x) or later!
* Saves artboards as JPEG - Quality: 12 & lossy WebP - Quality: 100
* Prompts for a save location and prompts for a prefix to be added to the artboard name (uses the doc name by default)
* Existing files will be silently overwritten
* Converts non-RGB to sRGB color space, RGB color mode and 8 bpc
* If RGB the original color space is retained, beware if using ProPhoto RGB!
* Added metadata removal for Document Ancestors, Camera Raw Settings and XMP
* Added a "script running" window to provide feedback during the script execution
* Creates a temporary working document

Instructions for saving and installing:
https://prepression.blogspot.com/2017/11/downloading-and-installing-adobe-scripts.html
*/

// Original script author below:

// =========================================================================
// artboardsToPNG.jsx - Adobe Photoshop Script
// Version: 0.6.0
// Requirements: Adobe Photoshop CC 2015, or higher
// Author: Anton Lyubushkin (nvkz.nemo@gmail.com)
// Website: http://lyubushkin.pro/
// =========================================================================

#target photoshop

    (function () {

        app.bringToFront();

        // Ensure that version 2022 or later is being used
        var versionNumber = app.version.split(".");
        var versionCheck = parseInt(versionNumber);
        if (versionCheck < 23) {
            alert("You must use Photoshop 2022 or later to save using native WebP format...");

        } else {
            if (app.documents.length !== 0) {

                var docRef = app.activeDocument,
                    allArtboards,
                    artboardsCount = 0;

                var outputFolder = Folder.selectDialog("Select an output folder to save the artboards as JPEG & WebP:");
                // Test if Cancel button returns null, then do nothing
                if (outputFolder === null) {
                    app.beep();
                    return;
                }

                var preFix = prompt("Add the prefix including separator character to the artboard name:", docRef.name.replace(/\.[^\.]+$/, '') + "_");
                // Test if Cancel button returns null, then do nothing
                if (preFix === null) {
                    app.beep();
                    return;
                }

                // Hide the Photoshop panels
                app.togglePalettes();

                if (outputFolder) {

                    // Dupe to a temp doc
                    app.activeDocument.duplicate(app.activeDocument.name.replace(/\.[^\.]+$/, ''), false);

                    // Clean out unwanted metadata
                    deleteDocumentAncestorsMetadata();
                    removeCRSmeta();

                    // If the doc isn't in RGB mode
                    if (activeDocument.mode !== DocumentMode.RGB)
                        // Convert to sRGB
                        activeDocument.convertProfile("sRGB IEC61966-2.1", Intent.RELATIVECOLORIMETRIC, true, false);
                    // Ensure that the doc mode is RGB (to correctly handle Indexed Color mode)
                    activeDocument.changeMode(ChangeMode.RGB);
                    activeDocument.bitsPerChannel = BitsPerChannelType.EIGHT;

                    allArtboards = getAllArtboards();
                    artboardsCount = allArtboards[0];

                    // Loop over the artboards and run the main function
                    for (var i = 0; i < artboardsCount; i++) {

                        // Script running notification window - courtesy of William Campbell
                        // https://www.marspremedia.com/download?asset=adobe-script-tutorial-11.zip
                        // https://youtu.be/JXPeLi6uPv4?si=Qx0OVNLAOzDrYPB4
                        var working;
                        working = new Window("palette");
                        working.preferredSize = [300, 80];
                        working.add("statictext");
                        working.t = working.add("statictext");
                        working.add("statictext");
                        working.display = function (message) {
                            this.t.text = message || "Script running, please wait...";
                            this.show();
                            app.refresh();
                        };
                        working.display();

                        // Call the main function
                        docRef.suspendHistory('Artboards to JPEG & WebP', 'main(' + i + ')');
                        app.refresh();
                        app.activeDocument.activeHistoryState = app.activeDocument.historyStates[app.activeDocument.historyStates.length - 2];

                        // Ensure Photoshop has focus before closing the running script notification window
                        app.bringToFront();
                        working.close();
                    }
                }

                app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);

                // Restore the Photoshop panels
                app.togglePalettes();

                // End of script notification
                app.beep();
                alert(artboardsCount + ' WebP and ' + artboardsCount + ' JPEG files saved to: ' + '\r' + outputFolder.fsName);

                // Open the output folder in Windows Explorer or the Mac Finder
                // outputFolder.execute();


                ///// FUNCTIONS /////

                // The main working function
                function main(i) {
                    selectLayerByIndex(allArtboards[1][i]);
                    // RegEx replace illegal filename characters with a hyphen
                    var artboardName = preFix + app.activeDocument.activeLayer.name.replace(/[:\/\\*\?\"\<\>\|\\\r\\\n.]/g, "_"); // "/\:*?"<>|\r\n" -> "-"

                    executeAction(stringIDToTypeID("newPlacedLayer"), undefined, DialogModes.NO);
                    executeAction(stringIDToTypeID("placedLayerEditContents"), undefined, DialogModes.NO);
                    app.activeDocument.selection.selectAll();
                    try {
                        ungroupLayers();
                    } catch (e) {
                        alert("There was an unexpected error with the ungroupLayers function!")
                        app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
                    }
                    crop();
                    saveAsJPEG(artboardName);
                    saveAsWebP(artboardName);
                    app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
                }

                function getAllArtboards() {
                    try {
                        var ab = [];
                        var theRef = new ActionReference();
                        theRef.putProperty(charIDToTypeID('Prpr'), stringIDToTypeID("artboards"));
                        theRef.putEnumerated(charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));
                        var getDescriptor = new ActionDescriptor();
                        getDescriptor.putReference(stringIDToTypeID("null"), theRef);
                        var abDesc = executeAction(charIDToTypeID("getd"), getDescriptor, DialogModes.NO).getObjectValue(stringIDToTypeID("artboards"));
                        var abCount = abDesc.getList(stringIDToTypeID('list')).count;
                        if (abCount > 0) {
                            for (var i = 0; i < abCount; ++i) {
                                var abObj = abDesc.getList(stringIDToTypeID('list')).getObjectValue(i);
                                var abTopIndex = abObj.getInteger(stringIDToTypeID("top"));
                                ab.push(abTopIndex);
                            }
                        }
                        return [abCount, ab];
                    } catch (e) {
                        alert(e.line + '\n' + e.message);
                    }
                }

                function selectLayerByIndex(index, add) {
                    add = undefined ? add = false : add
                    var ref = new ActionReference();
                    ref.putIndex(charIDToTypeID("Lyr "), index + 1);
                    var desc = new ActionDescriptor();
                    desc.putReference(charIDToTypeID("null"), ref);
                    if (add) desc.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("selectionModifierType"), stringIDToTypeID("addToSelection"));
                    desc.putBoolean(charIDToTypeID("MkVs"), false);
                    executeAction(charIDToTypeID("slct"), desc, DialogModes.NO);
                }

                function ungroupLayers() {
                    app.runMenuItem(stringIDToTypeID('ungroupLayersEvent'));
                }

                function crop() {
                    var desc1 = new ActionDescriptor();
                    desc1.putBoolean(charIDToTypeID('Dlt '), true);
                    executeAction(charIDToTypeID('Crop'), desc1, DialogModes.NO);
                }

                function saveAsJPEG(_name) {
                    removeXMP();
                    var jpgOptns = new JPEGSaveOptions();
                    jpgOptns.formatOptions = FormatOptions.STANDARDBASELINE;
                    jpgOptns.embedColorProfile = true;
                    jpgOptns.matte = MatteType.NONE;
                    jpgOptns.quality = 12;
                    activeDocument.saveAs(new File(outputFolder + '/' + _name + '.jpg'), jpgOptns, true, Extension.LOWERCASE);
                }

                function saveAsWebP(_name) {
                    var s2t = function (s) {
                        return app.stringIDToTypeID(s);
                    };
                    var descriptor = new ActionDescriptor();
                    var descriptor2 = new ActionDescriptor();
                    descriptor2.putEnumerated(s2t("compression"), s2t("WebPCompression"), s2t("compressionLossy")); // Lossy compression
                    descriptor2.putInteger(s2t("quality"), 100); // WebP Quality
                    descriptor2.putBoolean(s2t("includeXMPData"), true); // Include XMP metadata
                    descriptor2.putBoolean(s2t("includeEXIFData"), true); // Include EXIF metadata
                    descriptor2.putBoolean(s2t("includePsExtras"), true); // Include Ps Extras metadata
                    descriptor.putObject(s2t("as"), s2t("WebPFormat"), descriptor2);
                    descriptor.putPath(s2t("in"), new File(outputFolder + '/' + _name + '.webp'), true);
                    descriptor.putBoolean(s2t("lowerCase"), true);
                    descriptor.putEnumerated(s2t("saveStage"), s2t("saveStageType"), s2t("saveSucceeded"));
                    executeAction(s2t("save"), descriptor, DialogModes.NO);
                }

                function removeXMP() {
                    //https://community.adobe.com/t5/photoshop/script-to-remove-all-meta-data-from-the-photo/td-p/10400906
                    if (!documents.length) return;
                    if (ExternalObject.AdobeXMPScript === undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
                    var xmp = new XMPMeta(activeDocument.xmpMetadata.rawData);
                    XMPUtils.removeProperties(xmp, "", "", XMPConst.REMOVE_ALL_PROPERTIES);
                    app.activeDocument.xmpMetadata.rawData = xmp.serialize();
                }

                function removeCRSmeta() {
                    //community.adobe.com/t5/photoshop/remove-crs-metadata/td-p/10306935
                    if (!documents.length) return;
                    if (ExternalObject.AdobeXMPScript === undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
                    var xmp = new XMPMeta(app.activeDocument.xmpMetadata.rawData);
                    XMPUtils.removeProperties(xmp, XMPConst.NS_CAMERA_RAW, "", XMPConst.REMOVE_ALL_PROPERTIES);
                    app.activeDocument.xmpMetadata.rawData = xmp.serialize();
                }

                function deleteDocumentAncestorsMetadata() {
                    whatApp = String(app.name); //String version of the app name
                    if (whatApp.search("Photoshop") > 0) { //Check for photoshop specifically, or this will cause errors
                        if (ExternalObject.AdobeXMPScript === undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
                        var xmp = new XMPMeta(activeDocument.xmpMetadata.rawData);
                        // Begone foul Document Ancestors!
                        xmp.deleteProperty(XMPConst.NS_PHOTOSHOP, "DocumentAncestors");
                        app.activeDocument.xmpMetadata.rawData = xmp.serialize();
                    }
                }

            } else {
                alert('You must have a document open!');
            }
        }

    })();

 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Jun 06, 2024 Jun 06, 2024

@Stephen Marsh  

What a legend!! Thank you so so SO much for your help here. 
Really appreciated. 

Hope you have a wonderful day. 
Mel 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 06, 2024 Jun 06, 2024

@Mel30188944k1h7 

 

You’re welcome! I have updated the code to v1.1 with some extra metadata removal options to help slim down and or sanitise the output.

 

EDIT: Script updated to a 1.2 version with a "Script running, please wait..." message.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Jun 17, 2024 Jun 17, 2024

Hey @Stephen Marsh 

It's really helpful, thank you so much. One thing I noticed with these versions of the scripts, are that the document immediately closes after the script has finished running. Is it possible to keep the document open again since we have to export many different images. I think we had this on a previous version of the script, but then lost it again when you added the JPEG function to it. 

Any help would be greatly appreciated as always! Hope you have a wonderful week. 
Thanks Mel

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 18, 2024 Jun 18, 2024
quote

Hey @Stephen Marsh 

It's really helpful, thank you so much. One thing I noticed with these versions of the scripts, are that the document immediately closes after the script has finished running. Is it possible to keep the document open again since we have to export many different images. I think we had this on a previous version of the script, but then lost it again when you added the JPEG function to it. 

Any help would be greatly appreciated as always! Hope you have a wonderful week. 
Thanks Mel


By @Mel30188944k1h7

 

 

Temporary destructive changes are being made to the original file, so it is closed without saving for safety.

 

It is possible to add a step to duplicate the file, so that the script works on the duped file and then closes that down without saving, leaving the untouched original open.

 

Would that work for you?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Jun 18, 2024 Jun 18, 2024

Hi @Stephen Marsh 
Ahh I see, hmm. We don't need the file to save specifically. It's just the PSD.  Just to remain open so we can crop more images. It was definately possible in an older version of the script, but I can't seem to find where you made the change for me. 

If this is the only way to do it, that totally cool. Just saves us from having to reopen the PSD. 

Thanks a lot for your time! 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Jun 18, 2024 Jun 18, 2024

I guess the other thing to consider is that IF we wanted to make a small edit to the crop, we previously did, We'd have to add the images again. Keeping the file "OPEN" would allow us to edit as much as we needed to first before closing and saving the PSD if needed. I just had that exact issue.

I hope that makes sense? @Stephen Marsh 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Feb 20, 2025 Feb 20, 2025

Hey @Stephen Marsh ,

 

Amazing script-works very well, what a champ!

Just wondering if there's a chance where we can play around with the lossy before exporting them out as webp?

 

But other than that, what a life saver..!

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Feb 20, 2025 Feb 20, 2025
quote

Hey @Stephen Marsh ,

 

Amazing script-works very well, what a champ!

Just wondering if there's a chance where we can play around with the lossy before exporting them out as webp?

 

But other than that, what a life saver..!


By @Kezya Anatha

 

Thanks.

 

If you are referring to the lossy compression value, then yes! Change the following value from 100 to the required value:

 

descriptor2.putInteger(s2t("quality"), 100); // WebP Quality

 

P.S. Another prompt could be added to the script so that you can enter in the desired lossy quality value when the script is run, rather than having to edit the code.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Mar 11, 2025 Mar 11, 2025

MAAAAAN YOU SAVE MY LIFE TODAY! TANK YOU!!
SORRY CAPS, I`M VERY HAPPY FOR THIS!

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jul 07, 2023 Jul 07, 2023

THIS 100% NEEDS TO BE ADDED. So lame that this didn't get into Export as but did make it to Save A Copy... wah, wah, wah....

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 18, 2024 Jun 18, 2024

@Mel30188944k1h7 

 

I have updated the JPEG & WEBP script to a 1.3 version, working on and then closing a temporary file without closing the original doc.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Jun 18, 2024 Jun 18, 2024

AMAZING. Again thanks a lot for your support here. 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines