Skip to main content
Participating Frequently
September 18, 2025
Answered

Converting a bunch of artboards into separate Linked Smart Objects

  • September 18, 2025
  • 3 replies
  • 927 views

Hello all, 

I'm currently working on a huge file with numerous artboards. To lighten the file I'm converting each artboards into a smart object, into a linked smart object.

Since it's taking me some time to actually do it manually, I'm looking for a way to it automatedly create *linked smart objects* out of a selection. 

I'm on photoshop 2025 if that helps. 

 

Thanks for any help or clue 🙂

Arnault

Correct answer Stephen Marsh

@arnault_6305 

 

Rather than processing all artboards, this new script only works on (multiple) selected artboards.

 

EDIT: 22nd September, code updated to v1.2!

 

/* 
Convert Selected Artboards Content to Separate Linked Smart Objects.jsx
Stephen Marsh
v1.0 - 19th September 2025: Revised to work on selected artboards instead of all top-level layerSets
v1.1 - 20th September 2025: Corrected the layer selection code to include groups
v1.2 - 22nd September 2025: Added a progress bar for the artboard and .psb file processing loops
https://community.adobe.com/t5/photoshop-ecosystem-discussions/converting-a-bunch-of-artboards-into-separate-linked-smart-objects/m-p/15510949
Partly based on:
https://community.adobe.com/t5/photoshop-ecosystem-discussions/converting-to-linked-smart-object/m-p/13357539
Note: The linked smart object will have four guides indicating the original artboard bounds, which might be useful if you have content extending beyond the artboard edges.
*/

#target photoshop

app.activeDocument.suspendHistory("Convert Selected Artboard's Content to Separate Linked Smart Objects", "main()");

function main() {
    if (app.documents.length > 0) {
        var doc = app.activeDocument;
        var docName = doc.name.replace(/\.[^\.]+$/, '');

        ///// Process selected layers - from jazz-y (1st part) /////
        var s2t = stringIDToTypeID;
        (r = new ActionReference()).putProperty(s2t('property'), p = s2t('targetLayersIDs'));
        r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
        var lrs = executeActionGet(r).getList(p),
            sel = new ActionReference();
        /////

        // 1st Progress Bar: Artboards
        var win1 = new Window("palette", "Processing Selected Artboards", undefined, {closeButton: false});
        win1.orientation = "column";
        win1.alignChildren = ["fill", "top"];
        win1.margins = 15;

        var txt1 = win1.add("statictext", undefined, "Processing artboards...");
        txt1.alignment = "fill";

        var progressBar1 = win1.add("progressbar", undefined, 0, lrs.count);
        progressBar1.preferredSize = [300, 20];

        win1.show();

        var processedCount = 0;

        ///// Process selected layers - from jazz-y (2nd part) ///// 
        for (var i = 0; i < lrs.count; i++) {
            sel.putIdentifier(s2t('layer'), p = lrs.getReference(i).getIdentifier(s2t('layerID')));
            (r = new ActionReference()).putIdentifier(s2t('layer'), p);
            (d = new ActionDescriptor()).putReference(s2t("target"), r);
            executeAction(s2t('select'), d, DialogModes.NO);
        /////

            var currentLayer = doc.activeLayer;

            txt1.text = "Artboard " + (i + 1) + " of " + lrs.count + " - " + currentLayer.name;
            progressBar1.value = i;
            win1.update();

            if (isArtboard() === true) {
                artboardBounds(currentLayer, doc);
                deselectAllLayers();

                var selectedCount = selectAllLayersInGroup(currentLayer);

                if (selectedCount > 0) {
                    var newSOName = docName + "_" + currentLayer.name;
                    convertToEmbeddedSmartObject(newSOName);
                    convertToLinkedSmartObject();
                    processedCount++;
                } else {
                    alert("No layers found in artboard: " + currentLayer.name);
                }
            } else {
                alert("Unexpected error: Are all selected layers artboards?");
                win1.close();
                return;
            }

            progressBar1.value = i + 1;
            win1.update();
        }
        /////

        win1.close();

        // 2nd Progress Bar: PSB files
        var psbFiles = getPSBFiles(doc);
        if (psbFiles.length > 0) {
            var win2 = new Window("palette", "Processing PSB Files", undefined, {closeButton: false});
            win2.orientation = "column";
            win2.alignChildren = ["fill", "top"];
            win2.margins = 15;

            var txt2 = win2.add("statictext", undefined, "Processing PSB files...");
            txt2.alignment = "fill";

            var progressBar2 = win2.add("progressbar", undefined, 0, psbFiles.length);
            progressBar2.preferredSize = [300, 20];

            win2.show();

            for (var i = 0; i < psbFiles.length; i++) {
                txt2.text = "File " + (i + 1) + " of " + psbFiles.length + " - " + decodeURI(psbFiles[i].name);
                progressBar2.value = i;
                win2.update();

                processSinglePSB(psbFiles[i]);

                progressBar2.value = i + 1;
                win2.update();
            }

            win2.close();
        }

        app.beep();
        alert(processedCount + " artboards" + (processedCount === 1 ? "" : "s") + " processed to linked smart objects.");

    } else {
        alert("A document must be open to use this script!");
    }
}


///// Helper Functions /////

function getPSBFiles(doc) {
    try {
        var docName = doc.name.replace(/\.[^\.]+$/, '');
        var assetsFolder = new Folder(doc.path + "/" + sanitiseName(docName) + "_Assets");
        if (!assetsFolder.exists) return [];
        return assetsFolder.getFiles("*.psb");
    } catch (e) {
        alert("Error finding saved .psb files: " + e.toString());
        return [];
    }
}

function processSinglePSB(f) {
    try {
        var openedDoc = app.open(f);
        app.activeDocument.activeLayer = app.activeDocument.layers["artboardBounds"];
        executeAction(stringIDToTypeID("newGuidesFromTarget"), undefined, DialogModes.NO);
        app.activeDocument.activeLayer.remove();
        openedDoc.close(SaveOptions.SAVECHANGES);
    } catch (e) {
        alert("Error opening " + f.name + ": " + e.toString());
    }
}

function selectAllLayersInGroup(group) {
    var selectedCount = 0;
    var childLayers = group.layers;
    for (var j = 0; j < childLayers.length; j++) {
        var layer = childLayers[j];
        if (layer.typename === "ArtLayer" || layer.typename === "LayerSet") {
            selectLayerById(layer.id, selectedCount > 0);
            selectedCount++;
        }
    }
    return selectedCount;
}

function isArtboard() {
    try {
        var d = new ActionDescriptor();
        var r = new ActionReference();
        r.putEnumerated(stringIDToTypeID('layer'), stringIDToTypeID('ordinal'), stringIDToTypeID('targetEnum'));
        var options = executeActionGet(r);
        return options.hasKey(stringIDToTypeID('artboard'));
    } catch (e) {}
}

function sanitiseName(name) {
    return name.replace(/[\/\\:\*\?"<>\|]/g, "_");
}

function selectLayerById(id, add) {
    var ref = new ActionReference();
    ref.putIdentifier(charIDToTypeID("Lyr "), id);
    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 deselectAllLayers() {
    try {
        var idselectNone = stringIDToTypeID("selectNoLayers");
        executeAction(idselectNone, undefined, DialogModes.NO);
    } catch (e) {
        app.activeDocument.activeLayer = app.activeDocument.layers[0];
    }
}

function convertToEmbeddedSmartObject(newName) {
    try {
        layerLock("protectNone");
        executeAction(stringIDToTypeID("newPlacedLayer"), undefined, DialogModes.NO);
        app.activeDocument.activeLayer.name = sanitiseName(newName);
    } catch (e) {
        alert("Error converting to smart object: " + e.toString());
    }
}

function layerLock(lockValue) {
    var idapplyLocking = stringIDToTypeID("applyLocking");
    var desc1113 = new ActionDescriptor();
    var idnull = charIDToTypeID("null");
    var ref514 = new ActionReference();
    var idLyr = charIDToTypeID("Lyr ");
    var idOrdn = charIDToTypeID("Ordn");
    var idTrgt = charIDToTypeID("Trgt");
    ref514.putEnumerated(idLyr, idOrdn, idTrgt);
    desc1113.putReference(idnull, ref514);
    var idlayerLocking = stringIDToTypeID("layerLocking");
    var desc1114 = new ActionDescriptor();
    var idprotectNone = stringIDToTypeID(lockValue);
    desc1114.putBoolean(idprotectNone, true);
    desc1113.putObject(idlayerLocking, idlayerLocking, desc1114);
    executeAction(idapplyLocking, desc1113, DialogModes.NO);
}

function convertToLinkedSmartObject() {
    var doc = app.activeDocument;
    var docName = doc.name.replace(/\.[^\.]+$/, '');
    var assetsFolder = new Folder(doc.path + "/" + sanitiseName(docName) + "_Assets");
    if (!assetsFolder.exists) {
        assetsFolder.create();
    }

    var baseName = sanitiseName(doc.activeLayer.name);
    var fileName = baseName + ".psb";
    var file = new File(assetsFolder.fsName + "/" + fileName);

    var counter = 1;
    while (file.exists) {
        var suffix = "_" + ("000" + counter).slice(-3);
        fileName = baseName + suffix + ".psb";
        file = new File(assetsFolder.fsName + "/" + fileName);
        counter++;
    }

    var idplacedLayerConvertToLinked = stringIDToTypeID("placedLayerConvertToLinked");
    var desc346 = new ActionDescriptor();
    var idnull = stringIDToTypeID("null");
    var ref49 = new ActionReference();
    var idlayer = stringIDToTypeID("layer");
    var idordinal = stringIDToTypeID("ordinal");
    var idtargetEnum = stringIDToTypeID("targetEnum");
    ref49.putEnumerated(idlayer, idordinal, idtargetEnum);
    desc346.putReference(idnull, ref49);
    var idusing = stringIDToTypeID("using");
    desc346.putPath(idusing, file);
    executeAction(idplacedLayerConvertToLinked, desc346, DialogModes.NO);
}

function artboardBounds(group, doc) {
    var boundsLayer = group.artLayers.add();
    boundsLayer.name = "artboardBounds";
    doc.activeLayer = boundsLayer;
    var fillColor = new SolidColor();
    fillColor.rgb.red = 127;
    fillColor.rgb.green = 127;
    fillColor.rgb.blue = 127;

    doc.selection.selectAll();
    doc.selection.fill(fillColor);
    doc.selection.deselect();

    boundsLayer.opacity = 0;
    boundsLayer.fillOpacity = 0;

    boundsLayer.move(group.layers[0], ElementPlacement.PLACEBEFORE);
}

 

3 replies

Stephen Marsh
Community Expert
Stephen MarshCommunity ExpertCorrect answer
Community Expert
September 19, 2025

@arnault_6305 

 

Rather than processing all artboards, this new script only works on (multiple) selected artboards.

 

EDIT: 22nd September, code updated to v1.2!

 

/* 
Convert Selected Artboards Content to Separate Linked Smart Objects.jsx
Stephen Marsh
v1.0 - 19th September 2025: Revised to work on selected artboards instead of all top-level layerSets
v1.1 - 20th September 2025: Corrected the layer selection code to include groups
v1.2 - 22nd September 2025: Added a progress bar for the artboard and .psb file processing loops
https://community.adobe.com/t5/photoshop-ecosystem-discussions/converting-a-bunch-of-artboards-into-separate-linked-smart-objects/m-p/15510949
Partly based on:
https://community.adobe.com/t5/photoshop-ecosystem-discussions/converting-to-linked-smart-object/m-p/13357539
Note: The linked smart object will have four guides indicating the original artboard bounds, which might be useful if you have content extending beyond the artboard edges.
*/

#target photoshop

app.activeDocument.suspendHistory("Convert Selected Artboard's Content to Separate Linked Smart Objects", "main()");

function main() {
    if (app.documents.length > 0) {
        var doc = app.activeDocument;
        var docName = doc.name.replace(/\.[^\.]+$/, '');

        ///// Process selected layers - from jazz-y (1st part) /////
        var s2t = stringIDToTypeID;
        (r = new ActionReference()).putProperty(s2t('property'), p = s2t('targetLayersIDs'));
        r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
        var lrs = executeActionGet(r).getList(p),
            sel = new ActionReference();
        /////

        // 1st Progress Bar: Artboards
        var win1 = new Window("palette", "Processing Selected Artboards", undefined, {closeButton: false});
        win1.orientation = "column";
        win1.alignChildren = ["fill", "top"];
        win1.margins = 15;

        var txt1 = win1.add("statictext", undefined, "Processing artboards...");
        txt1.alignment = "fill";

        var progressBar1 = win1.add("progressbar", undefined, 0, lrs.count);
        progressBar1.preferredSize = [300, 20];

        win1.show();

        var processedCount = 0;

        ///// Process selected layers - from jazz-y (2nd part) ///// 
        for (var i = 0; i < lrs.count; i++) {
            sel.putIdentifier(s2t('layer'), p = lrs.getReference(i).getIdentifier(s2t('layerID')));
            (r = new ActionReference()).putIdentifier(s2t('layer'), p);
            (d = new ActionDescriptor()).putReference(s2t("target"), r);
            executeAction(s2t('select'), d, DialogModes.NO);
        /////

            var currentLayer = doc.activeLayer;

            txt1.text = "Artboard " + (i + 1) + " of " + lrs.count + " - " + currentLayer.name;
            progressBar1.value = i;
            win1.update();

            if (isArtboard() === true) {
                artboardBounds(currentLayer, doc);
                deselectAllLayers();

                var selectedCount = selectAllLayersInGroup(currentLayer);

                if (selectedCount > 0) {
                    var newSOName = docName + "_" + currentLayer.name;
                    convertToEmbeddedSmartObject(newSOName);
                    convertToLinkedSmartObject();
                    processedCount++;
                } else {
                    alert("No layers found in artboard: " + currentLayer.name);
                }
            } else {
                alert("Unexpected error: Are all selected layers artboards?");
                win1.close();
                return;
            }

            progressBar1.value = i + 1;
            win1.update();
        }
        /////

        win1.close();

        // 2nd Progress Bar: PSB files
        var psbFiles = getPSBFiles(doc);
        if (psbFiles.length > 0) {
            var win2 = new Window("palette", "Processing PSB Files", undefined, {closeButton: false});
            win2.orientation = "column";
            win2.alignChildren = ["fill", "top"];
            win2.margins = 15;

            var txt2 = win2.add("statictext", undefined, "Processing PSB files...");
            txt2.alignment = "fill";

            var progressBar2 = win2.add("progressbar", undefined, 0, psbFiles.length);
            progressBar2.preferredSize = [300, 20];

            win2.show();

            for (var i = 0; i < psbFiles.length; i++) {
                txt2.text = "File " + (i + 1) + " of " + psbFiles.length + " - " + decodeURI(psbFiles[i].name);
                progressBar2.value = i;
                win2.update();

                processSinglePSB(psbFiles[i]);

                progressBar2.value = i + 1;
                win2.update();
            }

            win2.close();
        }

        app.beep();
        alert(processedCount + " artboards" + (processedCount === 1 ? "" : "s") + " processed to linked smart objects.");

    } else {
        alert("A document must be open to use this script!");
    }
}


///// Helper Functions /////

function getPSBFiles(doc) {
    try {
        var docName = doc.name.replace(/\.[^\.]+$/, '');
        var assetsFolder = new Folder(doc.path + "/" + sanitiseName(docName) + "_Assets");
        if (!assetsFolder.exists) return [];
        return assetsFolder.getFiles("*.psb");
    } catch (e) {
        alert("Error finding saved .psb files: " + e.toString());
        return [];
    }
}

function processSinglePSB(f) {
    try {
        var openedDoc = app.open(f);
        app.activeDocument.activeLayer = app.activeDocument.layers["artboardBounds"];
        executeAction(stringIDToTypeID("newGuidesFromTarget"), undefined, DialogModes.NO);
        app.activeDocument.activeLayer.remove();
        openedDoc.close(SaveOptions.SAVECHANGES);
    } catch (e) {
        alert("Error opening " + f.name + ": " + e.toString());
    }
}

function selectAllLayersInGroup(group) {
    var selectedCount = 0;
    var childLayers = group.layers;
    for (var j = 0; j < childLayers.length; j++) {
        var layer = childLayers[j];
        if (layer.typename === "ArtLayer" || layer.typename === "LayerSet") {
            selectLayerById(layer.id, selectedCount > 0);
            selectedCount++;
        }
    }
    return selectedCount;
}

function isArtboard() {
    try {
        var d = new ActionDescriptor();
        var r = new ActionReference();
        r.putEnumerated(stringIDToTypeID('layer'), stringIDToTypeID('ordinal'), stringIDToTypeID('targetEnum'));
        var options = executeActionGet(r);
        return options.hasKey(stringIDToTypeID('artboard'));
    } catch (e) {}
}

function sanitiseName(name) {
    return name.replace(/[\/\\:\*\?"<>\|]/g, "_");
}

function selectLayerById(id, add) {
    var ref = new ActionReference();
    ref.putIdentifier(charIDToTypeID("Lyr "), id);
    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 deselectAllLayers() {
    try {
        var idselectNone = stringIDToTypeID("selectNoLayers");
        executeAction(idselectNone, undefined, DialogModes.NO);
    } catch (e) {
        app.activeDocument.activeLayer = app.activeDocument.layers[0];
    }
}

function convertToEmbeddedSmartObject(newName) {
    try {
        layerLock("protectNone");
        executeAction(stringIDToTypeID("newPlacedLayer"), undefined, DialogModes.NO);
        app.activeDocument.activeLayer.name = sanitiseName(newName);
    } catch (e) {
        alert("Error converting to smart object: " + e.toString());
    }
}

function layerLock(lockValue) {
    var idapplyLocking = stringIDToTypeID("applyLocking");
    var desc1113 = new ActionDescriptor();
    var idnull = charIDToTypeID("null");
    var ref514 = new ActionReference();
    var idLyr = charIDToTypeID("Lyr ");
    var idOrdn = charIDToTypeID("Ordn");
    var idTrgt = charIDToTypeID("Trgt");
    ref514.putEnumerated(idLyr, idOrdn, idTrgt);
    desc1113.putReference(idnull, ref514);
    var idlayerLocking = stringIDToTypeID("layerLocking");
    var desc1114 = new ActionDescriptor();
    var idprotectNone = stringIDToTypeID(lockValue);
    desc1114.putBoolean(idprotectNone, true);
    desc1113.putObject(idlayerLocking, idlayerLocking, desc1114);
    executeAction(idapplyLocking, desc1113, DialogModes.NO);
}

function convertToLinkedSmartObject() {
    var doc = app.activeDocument;
    var docName = doc.name.replace(/\.[^\.]+$/, '');
    var assetsFolder = new Folder(doc.path + "/" + sanitiseName(docName) + "_Assets");
    if (!assetsFolder.exists) {
        assetsFolder.create();
    }

    var baseName = sanitiseName(doc.activeLayer.name);
    var fileName = baseName + ".psb";
    var file = new File(assetsFolder.fsName + "/" + fileName);

    var counter = 1;
    while (file.exists) {
        var suffix = "_" + ("000" + counter).slice(-3);
        fileName = baseName + suffix + ".psb";
        file = new File(assetsFolder.fsName + "/" + fileName);
        counter++;
    }

    var idplacedLayerConvertToLinked = stringIDToTypeID("placedLayerConvertToLinked");
    var desc346 = new ActionDescriptor();
    var idnull = stringIDToTypeID("null");
    var ref49 = new ActionReference();
    var idlayer = stringIDToTypeID("layer");
    var idordinal = stringIDToTypeID("ordinal");
    var idtargetEnum = stringIDToTypeID("targetEnum");
    ref49.putEnumerated(idlayer, idordinal, idtargetEnum);
    desc346.putReference(idnull, ref49);
    var idusing = stringIDToTypeID("using");
    desc346.putPath(idusing, file);
    executeAction(idplacedLayerConvertToLinked, desc346, DialogModes.NO);
}

function artboardBounds(group, doc) {
    var boundsLayer = group.artLayers.add();
    boundsLayer.name = "artboardBounds";
    doc.activeLayer = boundsLayer;
    var fillColor = new SolidColor();
    fillColor.rgb.red = 127;
    fillColor.rgb.green = 127;
    fillColor.rgb.blue = 127;

    doc.selection.selectAll();
    doc.selection.fill(fillColor);
    doc.selection.deselect();

    boundsLayer.opacity = 0;
    boundsLayer.fillOpacity = 0;

    boundsLayer.move(group.layers[0], ElementPlacement.PLACEBEFORE);
}

 

Stephen Marsh
Community Expert
Community Expert
September 19, 2025

@arnault_6305 

 

Try this v1.0 code. Revisions can be made if needed.

 

EDIT: 22nd September, code updated to v1.2!

 

Note: The linked smart object will have four guides indicating the original artboard bounds, which might be useful if you have content extending beyond the artboard edges.

 

/* 
Convert All Artboards Content to Separate Linked Smart Objects.jsx
Stephen Marsh
v1.0 - 19th September 2025: Initial release
v1.1 - 20th September 2025: Corrected the layer selection code to include groups
v1.2 - 22nd September 2025: Added a progress bar for the artboard and .psb file processing loops
https://community.adobe.com/t5/photoshop-ecosystem-discussions/converting-a-bunch-of-artboards-into-separate-linked-smart-objects/m-p/15510949
Partly based on:
https://community.adobe.com/t5/photoshop-ecosystem-discussions/converting-to-linked-smart-object/m-p/13357539
Note: The linked smart object will have four guides indicating the original artboard bounds, which might be useful if you have content extending beyond the artboard edges.
*/

#target photoshop

app.activeDocument.suspendHistory("Convert All Artboard's Content to Separate Linked Smart Objects", "main()");

function main() {
    if (app.documents.length > 0) {
        var doc = app.activeDocument;
        var docName = doc.name.replace(/\.[^\.]+$/, '');
        var layerSets = doc.layerSets;

        // 1st Progress Bar: Artboards
        var win1 = new Window("palette", "Processing Artboards", undefined, { closeButton: false });
        win1.orientation = "column";
        win1.alignChildren = ["fill", "top"];
        win1.margins = 15;

        var txt1 = win1.add("statictext", undefined, "Processing artboards...");
        txt1.alignment = "fill";

        var progressBar1 = win1.add("progressbar", undefined, 0, layerSets.length);
        progressBar1.preferredSize = [300, 20];

        win1.show();

        var processedCount = 0;

        for (var i = 0; i < layerSets.length; i++) {
            var group = layerSets[i];

            txt1.text = "Artboard " + (i + 1) + " of " + layerSets.length + " - " + group.name;
            progressBar1.value = i;
            win1.update();

            artboardBounds(group, doc);
            deselectAllLayers();

            var selectedCount = selectAllLayersInGroup(group);

            if (selectedCount > 0) {
                var newSOName = docName + "_" + group.name;
                convertToEmbeddedSmartObject(newSOName);
                convertToLinkedSmartObject();
                processedCount++;
            } else {
                alert("No layers found in group: " + group.name);
            }

            progressBar1.value = i + 1;
            win1.update();
        }

        win1.close();

        // 2nd Progress Bar: PSB files
        var psbFiles = getPSBFiles(doc);
        if (psbFiles.length > 0) {
            var win2 = new Window("palette", "Processing PSB Files", undefined, { closeButton: false });
            win2.orientation = "column";
            win2.alignChildren = ["fill", "top"];
            win2.margins = 15;

            var txt2 = win2.add("statictext", undefined, "Processing PSB files...");
            txt2.alignment = "fill";

            var progressBar2 = win2.add("progressbar", undefined, 0, psbFiles.length);
            progressBar2.preferredSize = [300, 20];

            win2.show();

            for (var i = 0; i < psbFiles.length; i++) {
                txt2.text = "File " + (i + 1) + " of " + psbFiles.length + " - " + decodeURI(psbFiles[i].name);
                progressBar2.value = i;
                win2.update();

                processSinglePSB(psbFiles[i]);

                progressBar2.value = i + 1;
                win2.update();
            }

            win2.close();
        }

        app.beep();
        alert(processedCount + " artboards" + (processedCount === 1 ? "" : "s") + " processed to linked smart objects.");


    } else {
        alert("A document must be open to use this script!");
    }
}


///// Helper Functions /////

function getPSBFiles(doc) {
    try {
        var docName = doc.name.replace(/\.[^\.]+$/, '');
        var assetsFolder = new Folder(doc.path + "/" + sanitiseName(docName) + "_Assets");
        if (!assetsFolder.exists) return [];
        return assetsFolder.getFiles("*.psb");
    } catch (e) {
        alert("Error finding saved .psb files: " + e.toString());
        return [];
    }
}

function processSinglePSB(f) {
    try {
        var openedDoc = app.open(f);
        app.activeDocument.activeLayer = app.activeDocument.layers["artboardBounds"];
        executeAction(stringIDToTypeID("newGuidesFromTarget"), undefined, DialogModes.NO);
        app.activeDocument.activeLayer.remove();
        openedDoc.close(SaveOptions.SAVECHANGES);
    } catch (e) {
        alert("Error opening " + f.name + ": " + e.toString());
    }
}

function selectAllLayersInGroup(group) {
    var selectedCount = 0;
    var childLayers = group.layers;
    for (var j = 0; j < childLayers.length; j++) {
        var layer = childLayers[j];
        if (layer.typename === "ArtLayer" || layer.typename === "LayerSet") {
            selectLayerById(layer.id, selectedCount > 0);
            selectedCount++;
        }
    }
    return selectedCount;
}

function sanitiseName(name) {
    return name.replace(/[\/\\:\*\?"<>\|]/g, "_");
}

function selectLayerById(id, add) {
    var ref = new ActionReference();
    ref.putIdentifier(charIDToTypeID("Lyr "), id);
    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 deselectAllLayers() {
    try {
        var idselectNone = stringIDToTypeID("selectNoLayers");
        executeAction(idselectNone, undefined, DialogModes.NO);
    } catch (e) {
        app.activeDocument.activeLayer = app.activeDocument.layers[0];
    }
}

function convertToEmbeddedSmartObject(newName) {
    try {
        layerLock("protectNone");
        executeAction(stringIDToTypeID("newPlacedLayer"), undefined, DialogModes.NO);
        app.activeDocument.activeLayer.name = sanitiseName(newName);
    } catch (e) {
        alert("Error converting to smart object: " + e.toString());
    }
}

function layerLock(lockValue) {
    var idapplyLocking = stringIDToTypeID("applyLocking");
    var desc1113 = new ActionDescriptor();
    var idnull = charIDToTypeID("null");
    var ref514 = new ActionReference();
    var idLyr = charIDToTypeID("Lyr ");
    var idOrdn = charIDToTypeID("Ordn");
    var idTrgt = charIDToTypeID("Trgt");
    ref514.putEnumerated(idLyr, idOrdn, idTrgt);
    desc1113.putReference(idnull, ref514);
    var idlayerLocking = stringIDToTypeID("layerLocking");
    var desc1114 = new ActionDescriptor();
    var idprotectNone = stringIDToTypeID(lockValue);
    desc1114.putBoolean(idprotectNone, true);
    desc1113.putObject(idlayerLocking, idlayerLocking, desc1114);
    executeAction(idapplyLocking, desc1113, DialogModes.NO);
}

function convertToLinkedSmartObject() {
    var doc = app.activeDocument;
    var docName = doc.name.replace(/\.[^\.]+$/, '');
    var assetsFolder = new Folder(doc.path + "/" + sanitiseName(docName) + "_Assets");
    if (!assetsFolder.exists) {
        assetsFolder.create();
    }

    var baseName = sanitiseName(doc.activeLayer.name);
    var fileName = baseName + ".psb";
    var file = new File(assetsFolder.fsName + "/" + fileName);

    var counter = 1;
    while (file.exists) {
        var suffix = "_" + ("000" + counter).slice(-3);
        fileName = baseName + suffix + ".psb";
        file = new File(assetsFolder.fsName + "/" + fileName);
        counter++;
    }

    var idplacedLayerConvertToLinked = stringIDToTypeID("placedLayerConvertToLinked");
    var desc346 = new ActionDescriptor();
    var idnull = stringIDToTypeID("null");
    var ref49 = new ActionReference();
    var idlayer = stringIDToTypeID("layer");
    var idordinal = stringIDToTypeID("ordinal");
    var idtargetEnum = stringIDToTypeID("targetEnum");
    ref49.putEnumerated(idlayer, idordinal, idtargetEnum);
    desc346.putReference(idnull, ref49);
    var idusing = stringIDToTypeID("using");
    desc346.putPath(idusing, file);
    executeAction(idplacedLayerConvertToLinked, desc346, DialogModes.NO);
}

function artboardBounds(group, doc) {
    var boundsLayer = group.artLayers.add();
    boundsLayer.name = "artboardBounds";
    doc.activeLayer = boundsLayer;
    var fillColor = new SolidColor();
    fillColor.rgb.red = 127;
    fillColor.rgb.green = 127;
    fillColor.rgb.blue = 127;

    doc.selection.selectAll();
    doc.selection.fill(fillColor);
    doc.selection.deselect();

    boundsLayer.opacity = 0;
    boundsLayer.fillOpacity = 0;
    boundsLayer.move(group.layers[0], ElementPlacement.PLACEBEFORE);
}

 

  1. Copy the code text to the clipboard
  2. Open a new blank file in a plain-text editor (not in a word processor)
  3. Paste the code in
  4. Save as a plain text format file – .txt
  5. Rename the saved file extension from .txt to .jsx
  6. Install or browse to the .jsx file to run (see below)

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

 

Participating Frequently
September 19, 2025

Hello @Stephen Marsh,

I'm testing the script right now and it works wonderfully! 

Really, thank you so much!

Stephen Marsh
Community Expert
Community Expert
September 22, 2025

@Stephen Marsh- Hello Stephen, 

 

I'm back and have been able to test your script on my projects with similar features as the ones I've listed before : about 60 artboards ; 10 to 30 layers of all kind, some with and some without effects applied.  

 

So here's the exact results : 

- My file went from 12,4 Go to 447 Mo

- It took 9 mins to process every artboards

 

The file's size reduction is really amazing, and the time is perfectly fine, as it would take me twice the time to do manually. Furthermore, it allows me to work on other things in the meantime.

All in all, it's perfect for my usage 🙂 

 

Thanks a lot again!  

Arnault


@arnault_6305 

 

Due to the length of the processing time for your files, I have updated both scripts to v1.2 to now include a progress bar (one for the artboard conversion and another for the .psb post processing). This way you will know exactly where the script is at.

Stephen Marsh
Community Expert
Community Expert
September 18, 2025

I have two separate scripts that could be combined to do this. I'll look at it later when I have time.

 

So to be clear, each artboard should be retained, but the content of the artboard should be a single linked smart object.

 

Where should the linked .psb file be saved? The same folder as the main document? In a new sub folder?

 

One of the scripts:

 

https://community.adobe.com/t5/photoshop-ecosystem-discussions/converting-to-linked-smart-object/m-p/13357539#M685794

Participating Frequently
September 18, 2025

Hey, thank you so much.

Yes, each artboard should become a separate linked smart object. The idea is to extract the content of each artboard into its own .psb file, and then replace the artboard content in the main file with a linked smart object pointing to that .psb.

I’d like all the .psb files to be saved in a dedicated subfolder next to the main document. 

Appreciate any help if you get around to combining your scripts.

Stephen Marsh
Community Expert
Community Expert
September 18, 2025
quote

Hey, thank you so much.

Yes, each artboard should become a separate linked smart object. The idea is to extract the content of each artboard into its own .psb file, and then replace the artboard content in the main file with a linked smart object pointing to that .psb.

I’d like all the .psb files to be saved in a dedicated subfolder next to the main document. 

Appreciate any help if you get around to combining your scripts.


By @arnault_6305

 

That's doable, I have a rough version in progress, I should have something for you tomorrow.