Skip to main content

Module 6 β€’ Lesson 2🧰 Building a Panel / Plugin

A script buried in a menu is a tool only you will ever run. Wrap it in a UXP plugin β€” a real HTML/CSS/JavaScript panel docked in Photoshop β€” and it becomes something a whole team can click. This is how a personal automation becomes a shippable product.

πŸ“š What You'll Learn

By the end of this lesson, you will be able to:

  • Describe a UXP plugin: manifest + HTML panel + JS logic
  • Write a manifest.json declaring the panel and permissions
  • Build a panel UI and wire a button to a Photoshop action
  • Load and debug it with the UXP Developer Tool
  • Use batchPlay inside executeAsModal to do real work

⏱️ Estimated Time: 60 minutes

🎯 Project: A minimal working panel with one button that performs a real edit on the active document.

In This Lesson

πŸ‘€ The Goal: A Tool With a Face

Same logic, two front ends. On the right, a script you run by hunting through File β–Έ Scripts β–Έ Browse every time. On the left, a docked panel with a labelled button anyone on the team can click without knowing a line of code. The panel is the difference between a personal hack and a shippable tool. Drag the handle:

Panel Script file
A diagram of the principle. The logic is identical; the panel just gives it a permanent, discoverable home. That packaging is what turns "my script" into "our tool".

🧠 Mental Model: Three Files

A minimal UXP plugin is essentially three things: a manifest.json that tells Photoshop the plugin exists, what panel it adds, and what permissions it needs; an index.html that is the panel's UI (real HTML with UXP's spectrum widgets); and an index.js that runs when a button is clicked and does the Photoshop work. If you've built a tiny web page, you already know two-thirds of this.

The one Photoshop-specific rule: changes to the document must run inside executeAsModal (UXP's way of saying "I'm about to modify the doc, hold everything"). Inside that, you use the DOM (Lesson 6.1) or batchPlay. Everything else is ordinary front-end web development.

manifest.jsondeclares the panel+ permissions index.htmlthe panel UI(buttons, inputs) index.jsthe logic(does the work)
Figure 1: Three files. If you can write a web page with a button and a click handler, the only new idea is wrapping document edits in executeAsModal.

πŸ“„ The Manifest & the Panel

The manifest is the plugin's ID card. It names the plugin, declares a panel entrypoint, and requests permissions (like modifying documents). Here's a minimal one:

// manifest.json
{
  "id": "com.ray.deliverykit",
  "name": "Delivery Kit",
  "version": "1.0.0",
  "main": "index.html",
  "host": { "app": "PS", "minVersion": "24.0.0" },
  "entrypoints": [
    { "type": "panel", "id": "main", "label": { "default": "Delivery Kit" },
      "minimumSize": { "width": 180, "height": 240 } }
  ],
  "requiredPermissions": { "localFileSystem": "request" }
}

The panel itself is just HTML. UXP provides Spectrum widgets (sp-button, sp-slider) that look native:

<!-- index.html -->
<sp-heading>Delivery Kit</sp-heading>
<sp-button id="exportBtn" variant="cta">Export web sizes</sp-button>
<script src="index.js"></script>

⚠️ Declare only the permissions you use

UXP is sandboxed on purpose: a plugin can't touch the file system or network unless the manifest asks for it, and the user approves. Request the minimum you need β€” over-asking is a security smell that will get a plugin rejected from the marketplace and distrusted by users.

πŸ”Œ Wiring a Button

The logic file listens for the click and does the work inside executeAsModal. This handler bumps a document to a web size and exports it β€” the panel's whole job, in a dozen lines:

// index.js
const { app, core, action } = require("photoshop");

document.getElementById("exportBtn").addEventListener("click", async () => {
  await core.executeAsModal(async () => {            // required to modify the doc
    const doc = app.activeDocument;
    if (!doc) { return; }

    const longEdge = 2000;
    const scale = longEdge / Math.max(doc.width, doc.height);
    await doc.resizeImage(doc.width * scale, doc.height * scale);

    // batchPlay for a command not on the DOM (here: a JPEG export)
    await action.batchPlay([{
      _obj: "exportSelectionAsFileTypePressed",
      // …descriptor captured with the alchemist/ScriptingListener tool…
    }], {});
  }, { commandName: "Export web size" });
});

βœ… Debug live with the UXP Developer Tool

Adobe's free UXP Developer Tool loads your plugin folder straight into Photoshop, hot-reloads on save, and opens a Chrome-style devtools console for your panel. You build a UXP plugin the way you'd build a small web app: edit, save, see it update, read the console. That fast loop is what makes it approachable.

πŸ› οΈ Guided Build: A One-Button Panel

You'll scaffold the smallest plugin that does real work.

Step 1: Scaffold the files Β· 10 min

  1. Create a folder with manifest.json, index.html, and index.js from the snippets above.

Step 2: Load it Β· 10 min

  1. Open the UXP Developer Tool, Add Plugin, point it at your folder, and Load. The panel appears in Photoshop's Plugins menu.

Step 3: Make the button work Β· 14 min

  1. Wire the click to a simple, verifiable edit first (e.g., set the active layer's opacity to 50 via the DOM) so you know the plumbing works, then swap in the real task.

Step 4: Iterate Β· 6 min

  1. Edit, save, watch it hot-reload. Read the devtools console when something fails. Add a second button. πŸ†

βœ… Project Completion Checklist

  • ☐ manifest.json declares a panel + minimal permissions
  • ☐ index.html shows a titled panel with a button
  • ☐ index.js performs a real edit inside executeAsModal
  • ☐ Loaded and hot-reloaded via the UXP Developer Tool
  • ☐ Console used to debug at least one issue

πŸ§— Now You: Solo Variation

🌟 Your challenge

  1. Add a slider (sp-slider) that controls a value your button uses β€” e.g., the export long-edge size.
  2. Turn one of your Lesson 6.1 scripts into a panel button so it's one click instead of a menu hunt.
  3. Give the panel two buttons that run different tasks, and a status line that reports success or the error message.

Going further: package the plugin as a .ccx and install it as a real user would, or explore submitting to the Adobe marketplace β€” the path from tool to product.

🍳 Recipe Card: UXP Plugin

Manifest, panel, logic

  1. manifest.json: id, name, panel entrypoint, minimal permissions
  2. index.html: Spectrum widgets (sp-button, sp-slider)
  3. index.js: click handler β†’ executeAsModal β†’ DOM / batchPlay
  4. Load & hot-reload with the UXP Developer Tool; use the console
  5. Verify plumbing with a trivial edit before wiring the real task

Mantra: a panel is a web page that can edit your document.

πŸ““ Learning Journal

Add to your journal after this lesson:

  • Key concepts you learned
  • Techniques that clicked for you
  • Questions or confusion points to revisit
  • Ideas you want to try
  • Your progress and feelings about learning this

✍️ This lesson's prompt: Which of your scripts would most help your team if it had a button? Sketch its panel β€” the buttons, the one slider β€” before you build it.

πŸ”­ Where This Leads

You can give a tool a face; now you take the human out of the loop entirely. Next: Lesson 6.3 β€” Pipelines, running Photoshop headless on a server, calling APIs, and gating output with automated QA β€” the Module 6 deliverable.

Do I need to know web development for UXP?

Basic HTML/CSS/JS, yes β€” a UXP panel is a small web app. If you've built a page with a button and a click handler you're most of the way there; the Photoshop-specific parts (executeAsModal, batchPlay, the DOM) are a small, learnable layer on top.

What is batchPlay actually doing?

Replaying a low-level Photoshop command described as a JSON "descriptor" β€” the same mechanism as ExtendScript's Action Descriptors. You capture the descriptor with a listener tool while doing the action once by hand, then paste it into code. It's how you script features the tidy DOM doesn't expose.