Scripting Photoshop with JavaScript

Did you know you can script common Photoshop tasks with JavaScript? Now you do :)

IDE even

When you install PS it comes with a tool called ExtendScript Toolkit, which is an IDE to write scripts - with debugger, console to try stuff out etc.

To launch I just type "Extend" in Spotlight search. I'm guessing it's got to be somewhere in Program Files\Adobe on Windows too.

Here it is:

Getting started

As you can see on the screenshot, it's easy to get started.

app; // the Photoshop application
app.documents; // collection of open files/documents
app.documents[0]; // the first file
app.documents[0].name; // the first file's filename
app.documents[0].layers.length // how many layers in the doc

Running your script

There's a green "play" button in the IDE, it runs your script.

The directive:

#target photoshop

tells the engine that this is a photoshop script. Apparently you can script other Adobe products too.

If you don't have photoshop open, this directive will open it for you. Then it's all you - open a file, hide a layer, save, close, etc

Also if you save your script file with a .jsx extension, you can put in on the desktop and double-click it. Easy as pie.

And finally, if you put it in the appropriate directory (PSDIR/Presets/Scripts), it will show up in Photoshop menus under File -> Scripts

Docs

A *Yahoo* search will find the docs for you right here:
http://www.adobe.com/devnet/photoshop/scripting.html

There's a scripting guide (nice) and a JavaScript reference (the exact object/method names).

There are also VBScript and AppleScript references in case you insist on writing non-portable scripts :)

Generating code

Getting started with a completely new API can be a little uphill-y walk. For me it was especially so when it comes to saving a file with all the options. Worry not, you can generate code too!

Check the scripting guide where it talks about "The ScriptListener Plug-In". It's a plugin that you already have, you just copy it to to the appropriate directory to activate it and then restart PS.

Then whatever you do in photoshop will generate code in a .log file on your desktop. Then you can go back and cleanup that file (it generates somewhat verbose code) and get the parts you need.

Example - Mojotune chords

So for this "JavaScript is Everywhere" talk I gave this year at OSCON (slides) I came up (with a friend's help) with a sample application that I ported to different environments.

The core of it is m.js (m as in mojo, m as in (data) model). It's a portable piece of JavaScript that knows a lot about guitar chords. I wanted to generate nice PNGs with the data from the chord model. So - Photoshop scripting.

The document template is shown here with three visible layers.

This is the guitar's neck and each dot is where you press. The template has a layer for each dot (each fret on each string), appropriately named:

The task is to get the chord configuration, show the corresponding layers and save the file with the chord name as filename.

So I did this manually (while recording the generated code): show a layer, save the file.

Show/hide layers

The generated code for showing a layer looked like an oddly indented piece of overhead:

/////// GENERATED - DON'T!

// =======================================================
var idShw = charIDToTypeID( "Shw " );
    var desc2 = new ActionDescriptor();
    var idnull = charIDToTypeID( "null" );
        var list1 = new ActionList();
            var ref1 = new ActionReference();
            var idLyr = charIDToTypeID( "Lyr " );
            ref1.putName( idLyr, "11" );
        list1.putReference( ref1 );
    desc2.putList( idnull, list1 );
executeAction( idShw, desc2, DialogModes.NO );

This goes out the door since it can be replaced with:

app.documents[0].layers.getByName('11').visible = true;

(11 means first fret on first string)

Save for web

The generated code for saving a PNG is even worse, but I simply reindented it and shoved in into a function and that was that.

function saveFile(filename) {
    // the following is mostly auto-generated by PS 
    var idExpr = charIDToTypeID( "Expr" );
    var desc14 = new ActionDescriptor();

    /// 200 more lines...
}

All the chords

Finally the part that I actually had to write myself.

Directives:

#target photoshop
#include "../../lib/m.js"

Notice how easy it is to include a JS file, in my case the data model m.js?

Singe var pattern:

var data, 
    i, c, v, 
    layer,
    layers = app.documents[0].layers;

The mojo object comes from m.js. How it works is not very important. The thing is it has a mojo.guitar.getChord() method. You give it a chord name and it returns an array - which fret needs to be pressed on each string.

// mojo.guitar.getChord('Am')
// returns [ignore, 0, 1, 2, 2, 0, x]

All we need to do then is loop though the array and show the layers. Then save the file (e.g. Am.png) and then hide the same layers so they don't interfere the next chord. So this is the end result which generates all the chords m.js knows about:

for (c = 0; c < mojo.prettytones.length; c++) {
    for (v = 0; v < mojo.allchords.length; v++) {
        chord = mojo.prettytones[c].split('/')[0];
        chord += mojo.allchords[v];
        data = mojo.guitar.getChord(chord);
        for (i = 1; i <= 6; i++) {
            layer = "" + i + data[i];
            layers.getByName(layer).visible = true;
        }
        chord = chord.replace('#', '-sharp');
        saveFile('~/stoyan/mojo/chords/images/' + chord + '.png');
        for (i = 1; i <= 6; i++) {
            layer = "" + i + data[i];
            layers.getByName(layer).visible = false;
        }
    }
}

Run it and that's it - a bunch of PNG images are written.

Linkies

  • mojotune.jsx - The final script
  • m.js - the data model ("Check out Guitar George, he knows all the chords" - Dire Straits)
  • images - the generated chord PNGs
  • JS everywhere: slides and code

Sorry, comments disabled and hidden due to excessive spam. Working on restoring the existing comments...

Meanwhile you can find me on twitter - @stoyanstefanov