Introduction to Photoshop Javascript

I was thinking the other day about how I want to watermark the proof images that I give to clients. The watermark’s primary purpose it to protect the image from unwanted duplication. In this respect the watermark needs to be placed in the main subject area so that it cannot easily be cropped or cloned out. However, placing a watermark over the main subject can significantly impede the viewing experience. What to do? The image above shows the watermarking method that I’m currently using. I think it is prominent enough to protect the image yet subtle enough allow for good viewing during the photo selection process. This article describes the solution that I created and also serves as a quick and basic introduction the Photoshop JavaScript. I will present the script that I use and describe in detail what happens in the script. My goal is to convince you that writing JavaScript for photoshop is easy and can solve problems not possible with actions alone.
There are many commercial tools that will apply a watermark to your images. I even own a few already. They would probably do the job just fine, though I haven’t been able to recreate exactly this effect. The problem is that I’m the kind of person that would rather figure out how to do something myself than buy a dedicated tool for the purpose (within reason of course…I have no desire to rewrite Photoshop or many of the other tools that I use).
Photoshop provides many methods to create watermarking tools. Possibly the easiest method is to record an action to do it. Actions, however, have some limitations that leave them less than ideal. One drawback is that actions do not allow you to flexibly control the placement of the watermark depending on the orientation of the image. That is, I’d like the watermark placed about two-thirds of the way down the image and centered regardless of whether the image is portrait or landscape oriented. Another feature that I wanted, that actions would not allow, was to be able to watermark different sized images (web sized, or 4×6 print sizes) and have the font size automatically adjust for the image size. Since it became clear that actions were not going to get me where I wanted to be, I had to look elsewhere for the solution.
Photoshop provides three other methods for creating tools that could surely accomplish my needs: Filters, Automation Plugins, and Scripting. Filters and Automation plugins both require the Photoshop SDK and implementing them requires software developement tools and advanced programming experience. I have these tools and experience, but for a simple watermarking tool, this path seemed like overkill.
This left scripting. Photoshop can be scripted with AppleScript (on Apples only), VisualBasic (on PC’s only), and JavaScript (on both Apples and PC’s). I chose Javascript because it is platform independent and I didn’t require any of the features that could only be had with AppleScript or VisualBasic (the big difference being that JavaScript cannot interact with other applications).
Photoshop CS2 comes with extensive documentation and samples for the scripting engines. All of this can usually be found in the C:\Program Files\Adobe\Adobe Photoshop CS2\Scripting Guide directory. Also provided is the ExtendScript ToolKit which is an editor/debugger that runs alongside Photoshop to help develop and debug JavaScripts.
Photoshop CS2 comes with several scripts pre-installed. Probably the most well known is Dr. Brown’s ImageProcessor. These scripts are installed in the C:\Program Files\Adobe\Adobe Photoshop CS2\Presets\Scripts directory. You can simply drop any script files into this directory and they will show up on the File|Scripts menu ready to run.
Before I continue I just want to say that this article is written for the non-programmer. Any experienced programmer will probably find many of the explanations I give to be overly simplistic. This is intentional. I want the non-programmer to be able to get through this article and feel like they can write simple JavaScripts for photoshop. I’ll be skipping all the theory behind object oriented design. This definitely isn’t a complete reference to the JavaScript syntax or language. Please read the PDF documents supplied with Photoshop for all the details. I really just want to present enough basic information to peek the interest of those who might want to explore Photoshop JavaScript a little further.
So let’s design a watermarking tool….
The Watermarking Tool
In the software engineering discipline it is usually recommended (but not always practiced) that one develop a set of requirements that define a desired solution to a problem. This set of requirements defines the feature set and behavior of the solution. These are the simple requirements that I had for this watermarking tool:
- The solution must be recordable in an action so that I can batch process potentially hundreds of images. Scripts run from the File|Scripts menu can be recorded in an action.
- Customized placement of the watermark text anywhere on the image regardless of the image resolution and orientation.
- Customized watermark text.
- One or more styles of watermarks should be offered, or easily added. This way I can have a script for watermarking and others for adding a signature, filename, date, or metadata to an image.
Let’s discuss the first requirement for a moment. There are some special considerations here. Photoshop’s JavaScript engine allows one to develop sophisticated user interface dialog boxes. The ImageProcessor uses this feature to allow the user to enter all the information needed to run the script. It’s a very handy feature…as long as you don’t want to use the script in a batch process. Photoshop JavaScript User Interfaces (UI’s) are not "scriptable", meaning that when recording an action, Photoshop cannot know what you entered into the script dialog box, and thus cannot record that information as part of the action. This means that everytime you run the script, Photoshop is going to present the dialog box and wait for user input before continuing. This defeats the purpose of batch processing. So, basically, if you want to use the script in a batch environment, skip the dialog interface for entering data. Automation plugins are "scriptable", but that is a topic for a future article.
Requirement #2 is also worth explaining and is one of the main reasons why I’m using JavaScript instead of an action to apply my watermarks. Photoshop actions cannot make decisions based on the dimensions of the image. Photoshop does not provide for conditional logic in recrded actions. What does this mean? For example, it is impossible for an action to make a decision like "if the image is landscape then do this, otherwise do that". In my case, I want the watermark to be positioned at a specific location (3/4 of the way down and centered side-to-side) but an action will not allow me to do that for any general image. If all my images were portrait, 4×6, and 300 dpi, then an action would work, but they’re not. JavaScript can make decisions based on information about the image.
So how will this watermarking script be easily configured? I’ll cover this very soon…
Before I delve into the watermarking script I want to show how the watermark effect I’m using is created in Photoshop manually. The image on the first page of this article shows the effect. To create it I need to create a layer on top of the background image. This data on this layer is contained in a shape of the watermark text. The layer blend mode is set to screen to lighten the image, then the opacity is set to about 30% to reduce the lightening effect. And finally the Bevel & Emboss layer style is applied to given it some dimension. Here are the steps:
- Open an image and make sure it is flattened.
- Select the type tool, configure it to your liking (set the font size so it’s relatively large, select a font, select center alignment)
- Click on the image and type the word "proof".
- Use the move tool to position the text layer where you want it.
- Select the Layer|Rasterize|Type menu command to convert the text layer to a rasterized image of the text.
- Shift-click on the layer thumbnail for the rasterized type layer. This will load a selection in the shape of the text.
- Select the background layer by clicking on it.
- Type Ctrl-C, Ctrl-V to copy and paste a new layer with a copy of the image in the shape of the selection.
- Drag the old rasterized type layer to the garbage can because it is no longer needed.
- Select the new watermark layer and change the blend mode to screen and set the layer opacity to 30%.
- Double-click on the layer to open the layer styles dialog and apply the Bevel & Emboss style by clicking on the appropriate check-box. Just accept the default values and click OK.
- Flatten the image.
When I started converting this into a JavaScript I soon realized that steps 5, 6, and 7 did not translate easily. Specifically step 7 seems to be the show-stopper. In fact, I haven’t yet figured out how to do this in JavaScript. So I had to figure out a different way to accomplish the same thing. (If you know how to write the JavaScript for step 7 please let me know.) Here is the alternate method (to replace steps 5, 6, and 7, the changes are italicized):
- Open an image and make sure it is flattened.
- Select the type tool, configure it to your liking (set the font size so it’s relatively large, select a font, select center alignment)
- Click on the image and type the word "proof".
- Use the move tool to position the text layer where you want it.
- Select the Layer|Type|Create Work Path command from the main menu. This will create a work path in the shape of the text.
- Open the Path Palette, then from the palette flyout menu select Make selection…, set the feather radius to 0 and click OK.
- In the Path Palette, drag the work path to the garbage can to delete it. This has to be done in order for the copy-paste step below to work.
- In the Layer Palette, drag the text layer to the garbage can to delete it. The background layer should be the only layer left and the selection should still be the word "proof".
- Type Ctrl-C, Ctrl-V to copy and paste a new layer with a copy of the image in the shape of the selection.
- Select the new watermark layer and change the blend mode to screen and set the layer opacity to 30%.
- Double-click on the layer to open the layer styles dialog and apply the Bevel & Emboss style by clicking on the appropriate check-box. Just accept the default values and click OK.
- Flatten the image.
This process I do know how to convert into JavaScript…
The main difference between the to methods is that in the second method I use the text layer to create a work path and then use the work path to create a selection. Both the text layer and the work path are transitory in nature as they are deleted immediately after creating the selection.
The Watermarking Script
To make this article more useful I suggest that you download my watermarking script so that you can use it, edit it, play with it, and follow along with the rest of this article as I describe the inner workings. The script can be downloaded here (right-click and select Save target as…). A different version that demonstrates the Signature style watermark is available here (right-click and select Save target as…). Looking at the differences in these two scripts show how easily they are configured.
OK, let’s get started…
The first part of the script is the user configuration section (UCS). The JavaScript model doesn’t require or even know what a user configuration section is. It’s just what I call the first section. One of my principles (and many others do this as well) in writing scripts is to a place all of the configuration information into a single place in the file. The top of the file is a very good place to do this. This makes it very easy for someone to customize the tool without having to read through the entire script and decipher the code.
The UCS contains all the data that can be configured to customize the watermark. The intent with this script is that I could have several different versions of the script in the script directory. One for each kind of watermark, signature, or metadata application. I can simply create these by making a copy of an existing one and changing the information in the UCS.
Here is the user configuration section from the script:
//—————————————————
//———- START OF USER CONFIG SECTION ———–
//—————————————————
// Where to place watermark vertically (in percentage) 0.0 -> 1.0
var pctVertical = 0.75;// Where to place watermark horizontally (in percentage) 0.0 -> 1.0
// The watermark text will be centered horizontally at this point.
var pctHorizontal = 0.5;// Text Size: This value defines the font size of the watermark
// text in percentage of image height.
var textSize = 0.20;// Text Font: This string is the name of the font to use for the
// watermark text.
var textFont = "CopperplateGothic-Bold";// Watermark Text: This is the actual watermark text string.
var watermarkText = "PROOF";// Watermark Type: Define the watermark type to be applied.
// Types are: Proof, Signature
var watermarkType = "Proof";
This section basically defines several variables that are used later in the script to control the watermark application. Some comments are provided but I’ll discuss each in more detail. For the pupose of this article think of JavaScript variables as being either numbers or strings. Both types are valid but don’t try to mix them.
pctVertical & pctHorizontal
The pctVertical and pctHorizontal variables control where the watermark is placed on the image. The values are expressed in percent rather than in pixels. This simple decision allows the placement of the watermark to be independent of the resolution of the image. The percentages are expressed in a range from 0.0 (being the top or left edge) to 1.0 (being the bottom or right edge). Since I wanted my watermark to be placed three-quarters of the way down the image and centered side-to-side I used values of 0.75 for the pctVertical variable and 0.5 for the pctHorizontal. The vertical location is the baseline of the text, the horizontal location is the center of the watermark string.
textSize
The next configuration item in the UCS is the textSize variable. This item defines the size of the watermark text. It is expressed in percentage of the image height. Like the position variables, the percentage is expressed from 0.0 to 1.0. You might notice that the actual height of the text is not exactly the same as the specified percentage. For instance, I selected 0.20 (or 20%) for my watermark text height. When applied the text height is more like 15% of the image height. This apparent discrepency results from the complex font dynamics. The font size includes space below the baseline that is reserved for descenders (like the tail on a ‘p’ or ‘q’). If the watermark string doesn’t contain any descenders then that space will not be used. What this boils down to is that the textSize is approximate and can be fine tuned to obtain the desired effect. I chose 20% for my proof watermark because it makes the "proof" string fit well on both landscape and portrait oriented images.
textFont
Next is the textFont configuration variable. As the name implies, it defines the font that will be used for the watermark text. Ah, but if it were only so simple. The tricky part here is determining the name of the font. If I simply use the same name that I see in the font name drop down in Photoshop I will be dissapointed. Those names are "human readable" names which are, in most cases, not the same as the internal name used by Photoshop. Photoshop expects me to specify the internal name. But how do I figure out what the internal name is? Well, that’s another JavaScript (right-click and select Save target as…). This other script creates a text file that lists the normal name along side the internal name for all fonts availble to Photoshop. Here is a PDF file of the list of fonts installed on my system as an example. I don’t have any special fonts installed so this list is probably representative of many installs. To change the font used in the watermark script I simply look up the font name I want and enter the PostScript name in the script. Here is yet another script (right-click and select Save target as…) that you can run to get the name of the font on a currently active text layer. This script assumes that there is only a single font on the layer. You can save both of these files to the Photoshop scripts folder. Both of there scripts were found at the PS-Script website.
watermarkText
The next configuration variable is watermarkText. This variable specified the text that will be used for the watermark. For my proof image watermark I simply used the word "proof". For a signature I use my name.
watermarkType
The final configuration variable in this script is the watermarkType specifier. It specifies the type of watermark to be applied. In this case it is set to "Proof". The other type I’ve created is "Signature".
Next, the document preperation section…
Application & Document Preparation
This section of the script sets up a global variable that will be used in the remainder of the script to reference the active image document in Photoshop. Several properties of the image are stored and changed.
//—————————————————
//———- DOCUMENT PREPERATION SECTION ———–
//—————————————————
// Get a refernce to the current active document.
var docRef = app.activeDocument;
// Suppress all Photoshop dialogs
app.displayDialogs = DialogModes.NO;
var originalResolution = activeDocument.resolution;
var strtRulerUnits = app.preferences.rulerUnits;
var strtTypeUnits = app.preferences.typeUnits;app.preferences.rulerUnits = Units.PIXELS;
app.preferences.typeUnits = TypeUnits.PIXELS;
var resampleMethod = ResampleMethod.NONE;
activeDocument.resizeImage(null,null,72,resampleMethod );
The first line creates a variable called docRef. This variable is a reference to the currently active document object in Photoshop. We could simply use the app.activeDocument reference throughout the script but assigning a shorter name definitely improves readability.
The next statement tells the JavaScript engine to not display any Photoshop dialogs while executing the script. This is important since I want to be able to run the script in a batch process.
Next I’m going to save the current image resolution, ruler units and type units into temporary variables. I’ll use these temporary variables to restore the resolution, ruler units, and type units before the script exits back to Photoshop. The reason I do this is because I’m going to change the resoltuion, ruler units, and type units to values that I require in order for the calculations done later to be predictable and simple.
The statement "app.preferences.rulerUnits = Units.PIXELS;" tells photoshop that all units of measure will be expressed in pixels. When I want to know the image height by referencing the docRef.height property, the value will be in pixels. The next line accomplishes the same thing, but for text measurements. All references to text size will be in pixels. By having both the ruler units and text units expressed in pixels I do not need to worry about converting back and forth between differnt units in my calculations.
The last two lines are a round about way of changing the document resolution to 72dpi. The obvious way to do this would be to use the statement "activeDocuement.resolution = 72;". Unfortunately there is a bug in Photoshop CS2 that prevents this from working. It will essentially be ignored. So, instead, I have to resize the image to a 72dpi resolution. But note that the resampling method is set to NONE. This prevents any actually change to the image data. Only the resolution is updated. Why do I do this? There are some known, but not always well understood, issues when working with text at resolutions other than 72dpi. To be on the safe side, setting the resolution to 72dpi is a recommended method to avoid these issues. In reality, it may not make a difference in such a simple script as this.
Now the application and document environment is setup. Next, the JavaScript functions…
JavaScript Functions
The next section of the watermark script defines a JavaScript function that applies the effect that I like to use for my proof images. I’m not including all of it here, just the outer most layer that defines the function. I’ll delve into the function contents soon. Here is the basic function definition:
function applyProofWatermark()
{
// Lots of script code not shown here…
}
Functions (or procedures, or methods) are programming constructs that allow a programmer to collect code that performs a specific function into a container, and then easily reference that container by name in the script. So here I’ve created a function called applyProofWatermark().
One very good reason to create a function is if the code in the function is going to be used several times in the script. Without functions, the code would have to be replicated everytime. If this function contains a lot of code, then the script will become large and unmaintainable. Wrapping the code in a function makes maintenance much simpler. When an issue is found, it only needs to be fixed in one place.
This script only uses the function once so ease of maintenance was not my primary goal. The other reason to wrap the code in a function is when several different functions can be selected depending on a user configuration variable. In this case I have functions for my "Proof" watermark and my "Signature" watermark.
Let’s look at the inside of the applyProofWatermark()…
The applyProofWatermark() Function – 1
Now we’ll look at the inside of this JavaScript function one section at a time. The first section is similar to the user configuration section at the beginning of the script. It makes sense to have a similar section at the beginning of functions that allow the behavior of the function to be easily configured. This is the first section of the function codeL
// Screen Opacity: This value sets the layer opacity for the
// watermarking layer.
var screenOpacity = 30;
This section defines a variable that controls the general appearence of the watermark text. My proof watermark uses a screen layer blend mode to make the watermark text lighter than the image on which it lays. The screen_op variable defines the opacity of the screen layer. The opaciity can vary from 0 to 100. At 0 the watermark would be invisible. At 100, it would be too strong and damaging to the image.
The next section:
var posVertical = docRef.height * pctVertical;
var posHorizontal = docRef.width * pctHorizontal;
Here I’m simply defining a few more variables that will be used in creating the proof watermark. posVertical and posHorizontal define the vertical and horizontal position of the watermark text in inches. These values are calculated from the user configuration values pctVertical and pctHorizontal. The docRef symbol described earlier. The height and width are properties of the document (remember these are in inches because I set the ruler units to inches in the global configuration section). No matter what the width and height of the image is, these calculations will determine the proper location for the watermark in inches on the current image.
Now I’m going to build a text layer on top of the base image. This text layer will containg the watermark text, at the proper location, and at the proper size.
Here is the script code that accomplishes this:
var newTextLayer = docRef.artLayers.add();
newTextLayer.kind = LayerKind.TEXT;
newTextLayer.textItem.justification = Justification.CENTER;
newTextLayer.textItem.contents = watermarkText;
newTextLayer.textItem.font = textFont;
newTextLayer.textItem.position = Array(posHorizontal,posVertical);
newTextLayer.textItem.size = (docRef.height * textSize);
The first line in the snippet above creates a new layer in the document image. The docRef reference is again used to ensure that I’m adding the layer to the currently active document.
The next line let’s Photoshop know that this is going to be a Text Layer. And the following line sets the text justification to CENTER. Setting the justification to center is important because the position of the watermark is based on the text being centered horizontally at the specified location.
The next four lines change the properties of the text on the text layer. First I set the text that the layer will contain to be the text specified in the watermarkText configuration variable. The font is set according to the textFont configuration variable. The position and size are set as well.
The text size is a calculated value based on the textSize configuration variable and the image height. Remeber that I specified the textSize as a percentage of image height and that the image height is in pixels. Multiplying these two values give me the font size in pixels.
This text layer is just a temporary entity. It is just a means to an end. Next I’m going to convert the text to a path, then to a selection…
What I need in order to create the effect that I’m after is a selection mask in the shape of the watermark text. At this point the text is positioned and size properly but it is still just a plain text layer. The next few script statements take care of this for me:
newTextLayer.textItem.createPath();
newTextLayer.remove();docRef.pathItems.getByName("Work Path").makeSelection();
docRef.pathItems.getByName("Work Path").remove();
To create the selection mask from the text layer I need to create a path from the text and then make a selection from the path. The first line creates a path from the text on my text layer. Now that I have a path, I really no longer need the text layer. The next line deletes the text layer and leaves the background layer as the active layer.
The path that was created is automatically named "Work Path". The next two statements reference this path by name. The first statement creates a selection from the work path and the second one deletes the work path since it is no longer needed either.
After all of this I am left with a single background layer and a selection mask in the shape of the watermark text. Next I need to copy the selection and paste it into a new layer:
docRef.selection.copy();
docRef.paste();
The docRef.selection reference refere the current selection in Photoshop. The copy() method simply copies the selected image data on the active layer to the clipboard. The docRef.paste() method pastes the data on the clipboard into a new layer on the image. This process is exactly the same as manually selecting a region, then typing Ctrl-C, Ctrl-V to copy and paste the selection into a new layer. This newly pasted layer automatically becomes the active layer.
docRef.activeLayer.name = "WaterMark";
docRef.activeLayer.blendMode = BlendMode.SCREEN;
docRef.activeLayer.opacity = screenOpacity;
The first line here just gives the new layer a name. Probably not really necessary as the image will be flattened just before the script finishes (but at least now you know how to do it). The next two lines set the layer blend mode to screen and the layer opactiy to the value set in the user configuration variable screenOpacity.
There’s only one more step to finish off the proof style watermark, but it a big one…
The last step in creating my proof watermark is to apply the Bevel&Emboss layer style. Sounds easy enough, but unfortunately Photoshop doesn’t expose the ability to easily apply layer styles to the JavaScript engine. Instead, there is a long round-about process that involves installing a plugin called ScriptListener (it comes with Photoshop). When installed, this plugin listens to everything you do in Photoshop and creates a text file that contains arcane JavaScript code to replicate it.
Describing how to install and use the ScriptListener plugin is a bit beyond the scope of this article. Fear thee not. The Scripting Guide provided with Photoshop does a fine job of walking you through the process.
I did all the requisite steps and inserted the code into my script. Here is the first few and last few lines of the ScriptListner code that applies the Bevel&Emboss layer style to the active layer:
// This code applies the "Bevel and Emboss" layer style
// to the active layer. This code was generated using the
// ScriptListener plugin.
var id11 = charIDToTypeID( "setd" );
var desc4 = new ActionDescriptor();
var id12 = charIDToTypeID( "null" );
.
.
.
var id70 = charIDToTypeID( "Lefx" );
desc4.putObject( id18, id70, desc5 );
executeAction( id11, desc4, DialogModes.NO );
Like I said, this is pretty arcane stuff. If you really want to understand what’s going on here, then dig in and start learning. There is really very little information published (here’s some) that describes the output of the ScriptListener. Suffice it to say, for the purpose of this article, that this code does in fact apply the Bevel&Emboss layer style to the current style using the default values for all the layer style parameters. If you spend a little time looking at the code you’ll start to see values that correspond to the style parameters. You can easily replace these values with variable names to allow more control over the look of the watermark.
That’s the end of the applyProofWatermark function. There is a closing bracket for the function, but I’m not going to show it to you here. If you really want to see it, load the script in a text editor and take a look. Trust me though, it’s just a closing bracket. It looks like "}".
In the sample script that I provide there is also a function for applying a "Signature" watermark. I’m not going to describe here. I leave it as an exploration excercise.
o now I’ve setup my user configuration variables; setup the global environment; and defined a function for my proof watermark effect. Now I have to write the code to actually apply the effect to the active document…
The Main Script
Applying the proof watermark effect to the active document is as simple as calling the applyProofWatermark() function. I could simply add this line to the script immediately after the function definition. But wait. I created a watermarkType user configuration variable so that I could specify different types of watermarks. What I need now is code that will let me select the correct watermark according to the value of this variable. Here is the code that does this:
switch (watermarkType)
{
case "Proof":
applyProofWatermark();
break;case "Signature":
applySignatureWatermark();
break;
}
This bit of code demonstrates the conditional switch statement. It basically checks the value of the watermarkType variable against one or more possible matches. If a match is found, then the code below the matched case is executed until a break statement is seen. If no break statement occurs before the next case keyword, then the code in that case will also be executed. This bit of behavior is the cause of a great many bugs in many pieces of software so be careful.
So, essentially, if I’m applying a Proof watermark then this code will call the applyProofWatermark function and if I’m applying a Signature watermark then this code will call the applySignatureWatermark functions. Pretty straight forward.
All that’s left to do now is some basic cleanup and environmental restoration…
Environmental Cleanup
If only real environmental cleanup were this easy! Here’s the rest of the code in the script:
// We’re all done now. Just flatten the image.
docRef.flatten();
// Restore all the settings that we changed at the beginning. If
// we don’t do this then the user might get confused.
app.preferences.rulerUnits = strtRulerUnits;
app.preferences.typeUnits = strtTypeUnits
activeDocument.resizeImage(null,null,originalResolution,resampleMethod);// Cleanup the references we’ve made. This is just good programming
// practice.
docRef = null;
newTextLayer = null;
The first thing I’m going to do is flatten the image. I do this just in case any of the watermark functions created layers and did not delete or flatten then image. Executing the docRef.flatten() method is the same as selecting the Layer|Flatten Image menu command in Photoshop.
Next I’ll restore all the application and document settings that were overridden in the global setup section of the script. Since all the settings were saved in temporary variables it’s easy to restore them. Again, the PHotoshop JavaScript resolution bug requires that I "resample" the image to get it back to the original resolution.
Finally, the two references that were created by the script to reference the active document and the text layer that was created are set to null. This null is a special keyword that basically means "this object is no longer valid or needed". Behind the scenes the JavaScript interpreter does a lot of work to ensure that any memory used by these references is released back to the system. This will probably be done anyway when the script exits, but it is good programming practice to explicitly release the references.
Want to learn more about Photoshop JavaScript?
Online Resources
Here are some online resources that I found to be useful in learning about writing JavaScript for Photoshop:
The PS-Script Website
Photoshop Scripting at Kirupa.com
The Photoshop Scripting Forum at Adobe
Photoshop Scripting Forum at RetouchPro
PhotoshopSupport.com
And don’t forget about the Photoshop Scripting PDF documents that are already installed on your hardrive.
I hope you found this article to be interesting and informative. If you have any questions, comments, or suggestions for improving it please let me know in the comments.
Related posts:


