UXPScriptSparker

or: “Getting started with UXPScript in InDesign, and some very rough, seat of the pants speed comparisons with ExtendScript”.

2023-06-12: Important Note: the blog post below refers to InDesign 2023 18.3.

I’ve since been able to try out a prerelease of 18.4 and was shown some tweaks by Veena Datta Dasika from the Adobe team, and the timings are now very similar.

The remaining speed differences are very small and probably just statistical noise.

Repeated runs vary slightly in speed and sometimes ExtendScript gets the upper hand, sometimes UXPScript.

There are two important use cases:
– running pure JavaScript (without accessing the InDesign DOM)
– running code that accesses the InDesign DOM.

Based on my very limited, unscientific tests, I’d say that when it comes to pure JavaScript, UXPScript is a lot faster than executing the same code in ExtendScript.

InDesign DOM access was a lot slower in UXPScript in InDesign 18.3, but will be on a par between ExtendScript and UXPScript from InDesign 18.4 onwards.

UXPScriptSparker is set up to let me to run identical script code either in an ExtendScript engine or in the UXPScript engine.

When running a script with heavy InDesign DOM interaction, my rough measurements seem to indicate that running a script in the UXPScript could be about 3 times slower than running the exact same script in an ExtendScript engine.

UXPScriptSparker

To help me get my head around UXPScript and how it affects the scripting landscape around Adobe InDesign, I made UXPScriptSparker.

https://github.com/zwettemaan/UXPScriptSparker

It’s still early days, and there are a lot of loose ends to be tied and documentation to be written, but it is already working fairly well.

I started out with JSXSparker as the base, and transmogrified it into UXPScriptSparker.

JSXSparker is a generator/framework I created a few years ago to help budding scripters get started with ExtendScript.

https://github.com/zwettemaan/JSXSparker

There’s also CEPSparker:

https://github.com/zwettemaan/CEPSparker

I heavily restructured and rewrote large parts of JSXSparker to end up with UXPScriptSparker.

From past experience in the CEP environment, I have grown into the habit of writing a lot of reusable, ‘multi-purpose’ JavaScript code which can run both in ExtendScript and in CEP/JavaScript.

The disadvantage is that all my code for CEP panels looks like ‘old style’ JavaScript, but I am happy to pay that price for the convenience of reusability, having the exact same code and exact same API’s and libraries available both in ExtendScript and JavaScript.

For UXPSparker, I’ve taken a similar approach, and made it possible to write shared code that can run both in ExtendScript and UXPScript.

The UXPSparker framework contains two scaffolds: an ExtendScript scaffold, and a UXPScript scaffold. These two scaffolds take care of the nitty-gritty details of each environment and hide a lot of implementation details from the main script, allowing the script to be mostly unaware of what environment it is running in.

Part of these scaffoldings are a bunch of shared libraries and APIs, which hide differences between the environments from the main script.

For example, both scaffolds provide a method UXES.fileio.appendUTF8TextFile(), and this method works exactly the same in both scaffolds.

The ExtendScript scaffold is instrumented to use the VSCode/ExtendScript debugger, and the UXPScript scaffold can be debugged with the Adobe UXP Debugger Tool.

The useful part (i.e. the ‘meat’) of the script can be contained in one or more .js files that are written in such a way that the exact same .js file can be executed in UXPScript as well as in ExtendScript, at will.

By being careful and avoiding more modern facilities of JavaScript 🙁 , it’s possible to write reusable code that will run in either environment.

And here is where it gets interesting: as part of UXPSparker I wrote a little sample script that stretches InDesign’s muscle a bit.

This sample script creates a bunch of colored text frames and puts some text in it. Nothing special – my main aim was to have a lot of InDesign DOM interaction.

To try this out for yourself: download the UXPScriptSparker from Github:

Decompress the .zip and navigate into the Mac or Windows subfolder, depending on your platform.

On Windows you might have to contend with some blue ‘Windows protected your PC‘ warning dialogs from Windows Defender. Click More Info and Run Anyway when that happens.

On Mac, you need to de-quarantine the software. Right-click the initialSetupConfigApp.command and then click the Open button:

Then double-click the SparkerConfig or SparkerConfig.exe and configure the starter code. Pick the ColoredTextFrames option in the STARTERCODE dropdown.

Click Generate

If desired you can now remove the templates and retain only the generated code: navigate into the devtools folder and double-click detachFromUXPScriptSparker.bat or detachFromUXPScriptSparker.command

Now move the UXPScriptSparker-main folder into your Adobe InDesign Scripts panel. Start InDesign and select Window – Utilities – Scripts.

Right-click User and select Reveal in Explorer or Reveal in Finder.

Navigate into the Script Panel subfolder, and move the UXPScriptSparker-main there.

You should now see the UXPScriptSparker-main appear on the Script Panel in InDesign.

Double-click either run_as_ES.jsx or run_as_UXPScript.idjs.

Speed Comparisons

I used this sample script to run some speed comparisons, and got some surprising results.

My speed comparison was all about InDesign DOM interaction.

I fully expect UXPScript will prove to be a lot faster than ExtendScript when it comes to pure JavaScript execution speed. But pure JavaScript speed is not important to me.

In my line of work, the speed of DOM interaction is what is important: how fast can I make InDesign (or InDesign Server) do something?

Before anything else: none of these tests are very scientific, and need to be taken with a grain of salt.

I did not account for CPU speeds, numbers of cores, or amounts of memory, so comparisons between workstations don’t provide much useful info.

One thing I found is that repeatedly I got abysmally bad timings on multiple workstations.

Resetting the InDesign preferences seemed to cure that.

Not sure what exactly is going on, but clearly it’s possible for InDesign to get into some weird state where scripts run abnormally slow.

To avoid this issue, I reset the InDesign preferences on all the machines I used for testing.

All my Intel-based workstations are reasonably specced – i.e. SSDs, 32GB RAM or 64GB of RAM, and mid-range Intel CPUs. My M1 MacBook Air is a low-level model with only 7 GPU cores and 16GB of RAM.

First surprise: I found my lowly M1 MacBook Air ran rings around all my Intel-based machines, Mac and Windows alike.

Second surprise: with this particular sample script, UXPScript seems consistently about three times slower than ExtendScript in running the exact same script.

For example, it takes around 0.5s to execute the sample script with the ExtendScript engine on an M1 MacBook Air. Running the exact same script in the same instance of InDesign on the same computer with UXScript takes around 1.5s.

An Intel MacBook Pro clocks in at 2.5s running in the ExtendScript engine, and around 7.5s running in the UXPScript engine.

On one of my Windows VM machines, I got around 3.5s (ExtendScript) vs 11s (UXPScript).

So, by my rough measurements, running this particular script with heavy DOM interaction is about 3 times slower in the UXPScript engine than in an ExtendScript engine.