Copy link to clipboard
Copied
How can I combine layers that are out of group using scripts? Thanks.
@Delta2487 - Try this, no layers need to be selected, it works just as your screenshot:
/*
Merge Continuous Ungrouped Bottom Layers.jsx
Stephen Marsh
v1.0 - 19th November 2025: Initial release
https://community.adobe.com/t5/photoshop-ecosystem-discussions/select-and-combine-layers-outside-of-group/td-p/15597925
*/
#target photoshop
app.activeDocument.suspendHistory("Merge Continuous Ungrouped Bottom Layers", "processLayers()");
function processLayers() {
if (!app.documents.length) {
...
Thanks for the script, it does what I want, but what I really want is for it to combine all the layers that don't belong to a group, regardless of whether they are before or after any group.
SELECT AND MERGE: CASE 1
SELECT AND MERGE: CASE 2
By @Delta2487
¯\_(ツ)_/¯
EDIT: This is what you were really wanting but didn't properly describe:
1) Turn visibility of all layers off
2) Turn visibility on for all layers EXCEPT layer groups
3) Merge visible layers
4) Turn visibi
...Copy link to clipboard
Copied
The layers aren't highlighted/selected, so it's not exactly clear what the rules/expectations are.
Do you wish to select the layers? Or always merge 2 or more consecutive ungrouped layers at the bottom of the layer stack, regardless of layer selection? Or...
Copy link to clipboard
Copied
@Delta2487 - Try this, no layers need to be selected, it works just as your screenshot:
/*
Merge Continuous Ungrouped Bottom Layers.jsx
Stephen Marsh
v1.0 - 19th November 2025: Initial release
https://community.adobe.com/t5/photoshop-ecosystem-discussions/select-and-combine-layers-outside-of-group/td-p/15597925
*/
#target photoshop
app.activeDocument.suspendHistory("Merge Continuous Ungrouped Bottom Layers", "processLayers()");
function processLayers() {
if (!app.documents.length) {
alert("A document must be open to run this script!");
return;
}
var doc = app.activeDocument;
var rootLayers = doc.layers;
if (rootLayers.length === 0) return;
var bottomIndex = rootLayers.length - 1;
var layersToSelect = [];
for (var i = bottomIndex; i >= 0; i--) {
var lyr = rootLayers[i];
if (lyr.typename === "LayerSet") break;
if (lyr.parent !== doc) break;
layersToSelect.push(lyr);
}
if (layersToSelect.length === 0) {
alert("No continuous bottom layers found.");
return;
}
/*
// Reverse so the top-most of the contiguous layers
layersToSelect.reverse();
// Get the name of the top selected layer
var topSelectedName = layersToSelect[0].name;
*/
// Select the layers
selectByID(layersToSelect[0].id);
for (var j = 1; j < layersToSelect.length; j++) {
addToSelection(layersToSelect[j].id);
}
// Convert selected layers to a smart object
executeAction(stringIDToTypeID("newPlacedLayer"), undefined, DialogModes.NO);
// Rasterize the smart object
var idrasterizeLayer = stringIDToTypeID("rasterizeLayer");
var desc449 = new ActionDescriptor();
var idnull = charIDToTypeID("null");
var ref57 = new ActionReference();
var idLyr = charIDToTypeID("Lyr ");
var idOrdn = charIDToTypeID("Ordn");
var idTrgt = charIDToTypeID("Trgt");
ref57.putEnumerated(idLyr, idOrdn, idTrgt);
desc449.putReference(idnull, ref57);
executeAction(idrasterizeLayer, desc449, DialogModes.NO);
// Deselect the layer
app.runMenuItem(stringIDToTypeID('selectNoLayers'));
///// Helper functions /////
function selectByID(id) {
var ref = new ActionReference();
ref.putIdentifier(stringIDToTypeID("layer"), id);
var desc = new ActionDescriptor();
desc.putReference(stringIDToTypeID("null"), ref);
executeAction(stringIDToTypeID("select"), desc, DialogModes.NO);
}
function addToSelection(id) {
var ref = new ActionReference();
ref.putIdentifier(stringIDToTypeID("layer"), id);
var desc = new ActionDescriptor();
desc.putReference(stringIDToTypeID("null"), ref);
desc.putEnumerated(stringIDToTypeID("selectionModifier"),
stringIDToTypeID("selectionModifierType"),
stringIDToTypeID("addToSelection"));
executeAction(stringIDToTypeID("select"), desc, DialogModes.NO);
}
}
Copy link to clipboard
Copied
Thanks for the script, it does what I want, but what I really want is for it to combine all the layers that don't belong to a group, regardless of whether they are before or after any group.
SELECT AND MERGE: CASE 1
SELECT AND MERGE: CASE 2
Copy link to clipboard
Copied
Thanks for the script, it does what I want, but what I really want is for it to combine all the layers that don't belong to a group, regardless of whether they are before or after any group.
SELECT AND MERGE: CASE 1
SELECT AND MERGE: CASE 2
By @Delta2487
¯\_(ツ)_/¯
EDIT: This is what you were really wanting but didn't properly describe:
1) Turn visibility of all layers off
2) Turn visibility on for all layers EXCEPT layer groups
3) Merge visible layers
4) Turn visibility of all layers on
5) Deselect all layers
/*
Merge Ungrouped Layers.jsx
Stephen Marsh
v1.0 - 20th November 2025: Initial release
https://community.adobe.com/t5/photoshop-ecosystem-discussions/select-and-combine-layers-outside-of-group/td-p/15597925
*/
#target photoshop
app.activeDocument.suspendHistory("Merge Ungrouped Layers", "processLayers()");
function processLayers() {
if (!app.documents.length) {
alert("A document must be open to run this script!");
return;
}
var doc = app.activeDocument;
// Get the top ungrouped layer name
var rootLayers = doc.layers;
var topArtLayer = null;
for (var i = 0; i < rootLayers.length; i++) {
if (rootLayers[i].typename === "ArtLayer") {
topArtLayer = rootLayers[i];
break;
}
}
var targetLayerName = topArtLayer.name;
// Add a new dummy layer to the top of the stack
doc.artLayers.add()
// Deselect all layers
app.runMenuItem(stringIDToTypeID('selectNoLayers'));
// Turn visibility of all layers off
setAllVisibility(doc.layers, false);
// Turn visibility on for layers EXCEPT LayerSet groups
showNonGroupLayers(doc.layers);
// Merge visible layers
doc.mergeVisibleLayers();
// Rename the layer
doc.activeLayer.name = targetLayerName;
// Turn visibility of all layers on
setAllVisibility(doc.layers, true);
// Deselect all layers
app.runMenuItem(stringIDToTypeID('selectNoLayers'));
///// Helper functions /////
// Recursively toggle visibility for all layers
function setAllVisibility(layers, state) {
for (var j = 0; j < layers.length; j++) {
try {
layers[j].visible = state;
} catch (e) {}
if (layers[j].typename === "LayerSet") {
setAllVisibility(layers[j].layers, state);
}
}
}
// Turn visibility ON for non-group layers
function showNonGroupLayers(layers) {
for (var k = 0; k < layers.length; k++) {
var lyr = layers[k];
if (lyr.typename === "ArtLayer") {
try { lyr.visible = true; } catch (e) {}
} else if (lyr.typename === "LayerSet") {
// leave group visibility OFF
showNonGroupLayers(lyr.layers);
}
}
}
}
Copy link to clipboard
Copied
Thanks for the script, and sorry if I wasn't clear.
Copy link to clipboard
Copied
Thanks for the script, and sorry if I wasn't clear.
By @Delta2487
All good, it's hard to know when a screenshot that looks specific should be general.
That second script may be a bit slow when working with a large layer stack... You can try this 1.1 version which might be quicker as it only loops over the layers once:
/*
Merge Ungrouped Layers v1-1.jsx
Stephen Marsh
v1.0 - 20th November 2025: Initial release
v1.1 - 21st November 2025: Updated to run faster on large layer stacks (single recurse over all layers)
https://community.adobe.com/t5/photoshop-ecosystem-discussions/select-and-combine-layers-outside-of-group/td-p/15597925
*/
#target photoshop
app.activeDocument.suspendHistory("Merge Ungrouped Layers", "processLayers()");
function processLayers() {
if (!app.documents.length) {
alert("A document must be open to run this script!");
return;
}
var doc = app.activeDocument;
// Find the top-level ArtLayer name
var rootLayers = doc.layers;
var topArtLayer = null;
for (var i = 0; i < rootLayers.length; i++) {
if (rootLayers[i].typename === "ArtLayer") {
topArtLayer = rootLayers[i];
break;
}
}
if (!topArtLayer) {
alert("No ungrouped ArtLayer found.");
return;
}
var targetLayerName = topArtLayer.name;
// Add dummy ArtLayer
doc.artLayers.add();
// Deselect everything
app.runMenuItem(stringIDToTypeID('selectNoLayers'));
// Only root/top-level ArtLayers visible
setVisibilityRootOnly(doc.layers, true);
// Merge visible layers
doc.mergeVisibleLayers();
// Rename merged result
doc.activeLayer.name = targetLayerName;
// Restore full visibility
setAllVisibility(doc.layers, true);
// Deselect everything
app.runMenuItem(stringIDToTypeID('selectNoLayers'));
///// Helper functions /////
/*
Set visibility so that:
Root-level ArtLayers → visible
LayerSets → invisible
All children of LayerSets → forced invisible
*/
function setVisibilityRootOnly(layers, isRoot) {
for (var i = 0; i < layers.length; i++) {
var lyr = layers[i];
if (lyr.typename === "ArtLayer") {
if (isRoot) {
// At root level → visible
try { lyr.visible = true; } catch (e) {}
} else {
// Inside a group → force hidden
try { lyr.visible = false; } catch (e) {}
}
}
else if (lyr.typename === "LayerSet") {
// Always hide groups
try { lyr.visible = false; } catch (e) {}
// Recurse children — but now treated as non-root
setVisibilityRootOnly(lyr.layers, false);
}
else {
// Anything else → hidden
try { lyr.visible = false; } catch (e) {}
}
}
}
// Restore visibility fully
function setAllVisibility(layers, state) {
for (var j = 0; j < layers.length; j++) {
try { layers[j].visible = state; } catch (e) {}
if (layers[j].typename === "LayerSet") {
setAllVisibility(layers[j].layers, state);
}
}
}
}
Get ready! An upgraded Adobe Community experience is coming in January.
Learn more