What is the quickest way to iterate over all layers in a document?

I’ve been seeing very slow performance in an addon I am currently developing. I have tracked down the major slowdown to needing to iterate layers a lot.

While testing, I added a simple forEach iteration for layers just to see if the iteration or the code action for layers was the problem. And the below line runs at almost exactly the same speed.
Is there a faster way to check all the layers under a layer in UXP?

Sample code:
layer.layers.forEach((elem)=> {});

The above line takes 250ms to run one time, and I am doing this for multiple layers (and sublayers of layers). So I end up with a routine I was expecting to take a second maximum taking 60 plus seconds, which is rather painful to do on document select/open/close, or just about anytime except the start of a 5 hour long export (5 hour export being another huge pain point probably from the exact same issue).

1 Like

I remember a similar issue being discussed on the forums before.

I suspect you’re on windows right?
what version of PS?

I am using the latest version of PS. I have access to both Mac and PC, but right now am working on PC since the artists that will use the addon are only on Windows.

If you happen to locate a link to the other discussion I obviously missed it, so directions would be appreciated.

here

I latest beta changelog it is mentioned that there’s a performance fix for windows , this could be it but I’m not sure.

also someone mentioned the executing in modal could improve performance.

lastly using batchPlay could run faster but I’m not sure how that code would look. maybe look into multiGet

edit
would you try this method to get all layers and see if anything improves

1 Like

Modal massively improves performance. Outside modal I would be an old man before completing an export of all the assets. In modal, it’s a 5 hour process (which is now resumable because Photoshop likes to crash at least twice during the export process. I’d also love to know what I’m doing that could possibly crash Photoshop, because it’s a LOT of files, but it is pretty much a boring repetition process so little changes to the image, duplicate, reduce colors (which is why it was duplicated), save, close the new file without saving, and on to the next. But it does 1000s (not quite to millions, but well over 4 digit).

could you please share your code
or at least the export snippet

also I feel like a 5 hours task is not task fit for a PS plugin.
using a command line tool like imagemagick could be a better fit if possible.

I’ve used imagemagick before for other things. But I am not aware of any way to enable or disable layers of a PSD. I may try the Photoshop c/c++ SDK and see if that can do better.

And frankly, this task does 100,000 so it won’t be instant, but it should be an ideal automation task Boring repitition. And it shouldn’t take photoshop 5 hours to do this. More than 1/2 the time is layer iteration, which is sad considering it’s writing PSD files. It’s why I asked how I should (I assume the correct way will be faster than what I am doing now).

To put in perspective, I was floored when I killed it outside modal mode because it had run a day and wasn’t even close to finished with one of the sections. Usage of modal radically improved things, but didn’t fix things. I really was expecting a 30 minute process when I started this. I think the 5 hours is insane, and either there is a bug in the tool or I am doing something that Photoshop hates and it slows down to deal with it, but I have no idea what I could do differently as far as iteration goes. All the iteration methods I’ve tried have been slow, most MUCH slower than the layer.layers.forEach() {} that takes 250ms, and almost always takes that 250ms no matter the number of layers (5 or 250 sublayers both run @250ms). Since I was expecting a quick list in 1-2ms, this is a massive difference is speed than the expected.

Thanks for replying. I really appreciate the mention of the Beta. I’ll take a look at it today. If they fixed a bug affecting iteration, running beta could be the solution for now.

inspecting dom api functions I found that many of them use batchplay under the hood.

that’s why I think that using batchplay is always better/faster especialy for complicated tasks.

Thanks, I have already been using batchplay. It is the ONLY way I can do a conversion to indexed color with some chance the palette will be what I need. If there were loops and variables, I would be set, but as is I have to query the dom to set the values needed to run the batchplay, so I still get stuck with slow access. It is somewhat amusing that the actual downsampling and saving of the image is actually faster than the layer iteration (layer.layers, layer.name, and that is pretty much it for access).

Yes, I can confirm that. batchPlay is for low-level coding and DOM is for easy coding.

With low-level you can cache whatever data you want to and make it faster. But DOM will always ask Photoshop for fresh values even if nothing has changed and also have safeguards to solve PS quirks e.g. sometimes you must select the layer to allow an action.

Having a cache in DOM would be tricky.

thanks for the confirmation!
I was thinking about getting a list of all layers using batchPlay, I found an array called targetLayers in the document object that seemed to always include just one layer.

how would I iterate over all layers using pure batchPlay?

Currently “multiGet” is a safe bet. https://twitter.com/J_Bereza/status/1643603589649113089

There might be one more option later on for having a tree structure instead of a linear one.

1 Like

Since I’m putting together this, I’ll post my finished code that does it.
As an aside, 2mins already dropped to 0.032 seconds. DOM has WAY too much overhead.

You wouldn’t happen to know how to get the background of the document via get or multiGet?
I also got some layers I’m not familar with added to the ones that are shown. What are the layers named <layer group> for?

  1. for bg layer you have to change the index based on the background layer presence
  2. since layers are stored in linear order rather than a tree it acts similar to HTML tags. You have pseudo-layers marking the end of the layer group. It is good only to know where layer group is. You can ignore it for everything else. Or you could use the parent ID property instead to assign layers into the correct group and ignore group ends entirely.

Also artboards are pretending being a groups same as a frame layers.

Thank you for the reply. 2. made sense and since I am putting things into a tree, I will end up removing those. Is there an indicator besides the name that this is what they are?

For 1. I am afraid that I don’t understand well enough. What index, where? Could you perhaps provide a sample command snippet that would get items from the background (or from the background and all other layers)?

ad 2) yes I think one property is layer kind and another something like layer section property? I would personally avoid layer name property to detect it.

ad 1) I think Alchemist takes background existence into consideration when it generates the source code. Get one snippet with background layer in document and one snippet without it and you should see a difference :slight_smile: It is index within extendedReference

Also you can use exposed classes to instantiate Layer by layer ID and document ID https://developer.adobe.com/photoshop/uxp/2022/ps_reference/media/prototype/ and use DOM features. So you can have blazing fast DOM traversing and for a few found layers use DOM API.

So you could write something like const layer = new app.Layer(docID, layerID) and then layer.opacity = 50 (not sure if docID is first or second argument)

It will be slower than optimized batchPlay but maybe performance gain will not be worth it in your case extra effort.

So, the latest beta has radically improved iteration and setting simple properties (visible). Batchplay isn’t much faster than beta for that use case, but it still massively outperforms for most of the rest of the operations I needed to do (such as duplicate, change mode, save, and close again and again). But at least for basic iteration, a basic for loop using either index or of seems to be acceptably performant now. Beta is seconds to update (2ish) versus the 2 minutes+ that the latest release was just for iteration. So while the complete process is still not supper fast, it’s way down from 5+ hours for the set visibility, write out, reset visibility, and do for next image loop.