Photoshop Hybrid Plugins: UXP + Native C++

The Photoshop-specific extension architecture that combines a UXP front-end with a native C++ back-end — modern JavaScript and Spectrum UI for the panel, compiled C++ for the work that has to be fast. The right answer for production extensions that need both.

← Back to Blog

Photoshop Hybrid Plugins — UXP front-end plus native C++ back-end

The problem Hybrid Plugins solve

Photoshop’s extension story for most of the last decade has had a gap. UXP gives you modern JavaScript, Spectrum components, and a clean panel API — but JavaScript hits performance ceilings for image processing kernels, large-batch work, and anything that needs to integrate with native libraries. The C++ SDK gives you native speed and full Photoshop API access — but the UI story is dialogs and native widgets, not the modern panel experience users expect. CEP plus ExtendScript bridged the two for years, but at the cost of architectural complexity, performance overhead, and a stack that’s now legacy.

Hybrid Plugins close the gap. A single plugin ships with two parts: a UXP front-end (the panel users see) and a native C++ back-end (the engine doing the work). The UXP side handles UI, configuration, orchestration, async network calls, file dialogs. The C++ side handles per-pixel processing, custom file formats, GPU work, integration with native libraries. The two communicate through a JavaScript-to-native bridge that’s much faster and cleaner than CEP’s ExtendScript bridge ever was.

This is the strategic architecture for new production Photoshop extensions in 2026. It exists for Photoshop and (as of writing) only Photoshop — Illustrator and InDesign developers needing the equivalent capability fall back to writing two separate plugins that communicate at the Photoshop API level.

What you can do in each half

The UXP front-end

The JavaScript side of a Hybrid Plugin has the same capabilities as any UXP plugin:

  • Modern JavaScript with async/await, modules, classes
  • Spectrum Web Components for native-looking Adobe UI
  • Photoshop DOM API for document manipulation
  • fetch for HTTPS calls, localStorage for plugin state, file system access via UXP’s permissioned APIs
  • Hot reload during development via the UXP Developer Tool
  • Standard UXP packaging and distribution as a .ccx file

For most user interaction, configuration UI, and orchestration logic, the UXP side is everything you need.

The C++ back-end

The native side has the same capabilities as a standalone Photoshop C++ plugin:

  • Full access to Photoshop’s C++ SDK — per-pixel processing, layer manipulation at native speed, file format SDK
  • Integration with native libraries — OpenCV, ML inference engines (CoreML, TensorRT, ONNX Runtime), custom hardware drivers, codec libraries
  • GPU access via Metal (macOS) or DirectX/Vulkan (Windows)
  • Access to operating-system APIs UXP’s sandbox doesn’t expose
  • Full standard library plus any C++ dependency the build chain can compile

The bridge between them

UXP’s native module support lets the JavaScript side load a compiled C++ module and call its functions as if they were JavaScript functions. The communication is in-process — no IPC, no serialisation overhead, just JavaScript values crossing into C++ via a binding layer Adobe provides. Function calls from JavaScript to C++ have similar overhead to a regular JavaScript function call, which is the architectural improvement over CEP’s string-passing eval bridge.

The binding layer handles type conversion: JavaScript numbers become C++ doubles or ints, JavaScript strings become C++ std::string or const char*, JavaScript arrays of numbers become C++ vectors. Image data is passed by reference rather than copied — the JavaScript side gets a handle to a Photoshop image surface, and the C++ side reads/writes the pixel data directly.

When Hybrid Plugins are the right choice

Performance-critical image processing

Custom filters, retouching pipelines, AI-driven enhancement — anything that touches every pixel of an image and needs to do so quickly. The JavaScript engine in UXP is fast for application logic but not competitive with C++ for tight pixel loops. A Hybrid Plugin lets the user-facing controls live in JavaScript while the actual filter runs at native speed.

Custom file format support

Importing or exporting a proprietary or specialised image format that Photoshop doesn’t handle natively — medical imaging formats, scientific data formats, legacy CAD formats. The C++ SDK has the file format APIs; UXP provides the modern UI for picking files, configuring options, displaying progress.

Native library integration

Plugins that wrap OpenCV (for computer vision operations), ML inference engines (for AI-driven features), or custom hardware drivers (for medical scanners, industrial cameras, specialised imaging hardware). The native libraries can’t run in UXP’s JavaScript sandbox; the C++ back-end gives them a host.

CEP migrations with Node.js dependencies

Existing CEP panels that depended on Node.js native modules (compiled npm packages, FFI bindings to system libraries) can’t port directly to plain UXP — UXP doesn’t provide Node.js. A Hybrid Plugin gives you the migration path: the JavaScript side ports the CEP panel UI to UXP and Spectrum; the C++ side replaces the Node.js native modules with a compiled equivalent.

GPU-accelerated operations

Real-time previews, large-image scaling, multi-layer composites that need to run on the GPU rather than CPU. UXP’s JavaScript can’t directly access the GPU; the C++ back-end can use Metal or DirectX/Vulkan for hardware-accelerated work, with the UXP UI surfacing the controls and progress.

The architecture in practice

A typical Hybrid Plugin folder structure:

com.mapsoft.demo-hybrid/
  manifest.json           # UXP plugin manifest, declares native module
  index.html              # panel HTML
  js/
    main.js               # UXP front-end logic
  native/
    src/
      bridge.cpp          # the C++ side, exports functions to JS
      kernel.cpp          # the actual image processing
    build/
      darwin/lib.dylib    # compiled for macOS
      win32/lib.dll       # compiled for Windows
  package.json            # JavaScript dependencies
  CMakeLists.txt          # native build configuration

The JavaScript side loads the native module:

// main.js
const native = require('../native/build/lib');

document.getElementById('apply').addEventListener('click', async () => {
  const photoshop = require('photoshop');
  const doc = photoshop.app.activeDocument;
  // Get a pixel buffer for the active layer
  const buffer = await getPixelBuffer(doc.activeLayer);
  // Call into native code; the C++ side processes the buffer in-place
  native.applyKernel(buffer, { strength: 0.5, radius: 8 });
  await commitPixelBuffer(doc.activeLayer, buffer);
});

The C++ side exports functions:

// bridge.cpp (simplified)
#include "uxp_bridge.h"
#include "kernel.h"

UXP_EXPORT(applyKernel)(UXPArgs args) {
  PixelBuffer* buf = args.getBuffer(0);
  KernelParams params = args.getObject<KernelParams>(1);
  apply_kernel(buf, params);  // the actual C++ work
  return UXPValue::null();
}

This is illustrative rather than exact — the real Adobe SDK headers and binding macros are richer — but the shape is right: declarative function exports on the C++ side, normal JavaScript function calls on the UXP side.

Build pipeline

A Hybrid Plugin needs to compile its native module for both Windows and macOS as part of the package step. Standard tooling:

  • CMake as the build configuration, with platform-conditional sections for Windows MSVC and macOS Clang
  • GitHub Actions or equivalent CI for cross-platform compilation — Windows runners build the .dll, macOS runners build the .dylib, both artifacts assembled into the final package
  • Code signing on macOS (with notarization for distribution) and Authenticode on Windows — required for enterprise deployment and for some Marketplace channels
  • UDT (UXP Developer Tool) for local development, with the native module rebuilt on save via a CMake watch task

The development cycle is slower than pure UXP because every native code change triggers a recompile. Production teams typically have a tight inner loop where most iteration happens in JavaScript (with the native module stable) and a slower outer loop for native changes.

Distribution

Same channels as standard UXP plugins:

  • Adobe Marketplace — the official channel; reaches the largest audience but requires Adobe’s review of the native module for security and stability
  • Direct download — sell or distribute the .ccx file from your own site; right for niche enterprise tools
  • Enterprise side-loading — deploy .ccx packages across an organisation via SCCM, Jamf, or Intune

The .ccx file bundles the JavaScript, the manifest, and the platform-specific native binaries. Users see a single plugin with no awareness of the hybrid architecture underneath; the UXP runtime loads the appropriate native module on plugin start.

What CEP+ExtendScript taught us

The CEP+ExtendScript pattern was the previous attempt at "modern UI plus deep host integration", and it had real architectural problems:

  • String-passing bridge. CEP serialised every host call as a string passed to the ExtendScript engine. Performance overhead was meaningful, and the type system erosion (everything became strings, then was parsed back) was a constant source of bugs.
  • Two runtimes. CEP ran a Chromium webview, ExtendScript ran a separate ES3 engine. Memory footprint was high, and developers had to context-switch between two different JavaScript dialects.
  • Distribution complexity. CEP plugins shipped as ZXP files; if the ExtendScript depended on Node.js native modules, those modules shipped separately and loaded into the CEP panel’s background process.

Hybrid Plugins fix all three. One runtime (UXP’s JavaScript), one binding layer to native code (the UXP native module API), one package (a single .ccx with the native binaries inside). The architecture is genuinely simpler, despite covering more ground than CEP+ExtendScript could.

The honest take

Hybrid Plugins are the most important architectural change in Photoshop extensibility since UXP itself. For production extensions that need both modern UX and native performance, the architecture is now coherent: UXP for UI, C++ for work, native bindings for the hand-off, single .ccx for distribution. The pattern that previous Photoshop developers had to assemble themselves out of CEP plus side-car C++ plugins is now a first-class story.

For new Photoshop projects, the decision tree is: pure UXP for panels that don’t need native code; Hybrid Plugins for panels that do. Pure C++ SDK is still the right answer for filters and file format plugins where there’s no UI requirement, and for headless deployments via the Photoshop API — but the cases where a developer would have built a CEP panel plus a separate C++ plugin five years ago are now Hybrid Plugins.

Related Articles

Photoshop Extension Technologies

The five-way comparison of ExtendScript, UXP, CEP, Hybrid Plugins, and the C++ SDK — with the decision flow for picking the right one.

UXP vs CEP Extensions in Adobe Photoshop

The narrower comparison — just UXP and CEP — with deeper architectural detail and migration guidance.

Photoshop API for Cloud-Scale Image Processing

The cloud service for headless Photoshop — a different way to deliver native-performance image work, server-side.

Building a Hybrid Plugin?

Mapsoft has built C++ plugins, UXP panels, and Hybrid Plugins for Photoshop production workflows for over twenty years — from medical imaging filters to AI-driven retouching panels.

Get Adobe Photoshop →

Mapsoft is an Adobe affiliate. We may earn a commission on Adobe purchases made through these links, at no extra cost to you.