Adobe UXP plugin development is a trap. The documentation makes it look straightforward: HTML + CSS + JavaScript, package it up, deploy to Creative Cloud. What they don’t tell you is that the sandbox will break everything you think you know about web development.
Here’s what went wrong in v1, how v2 fixes it, and why I ended up building a browser-based creative platform instead.
The Original Plan (v1)
Brand.ai started simple: a Photoshop/InDesign plugin that analyzes creative work and provides AI-powered brand feedback in real-time.
The architecture was straightforward:
- UXP panel inside Adobe CC
- Claude API for brand analysis
- Direct file system access to read PSD/INDD files
- Local brand guideline database
// This looked so simple
const analysis = await fetch('/api/brand/analyze', {
method: 'POST',
body: JSON.stringify({ file: currentDocument })
});
What could go wrong?
UXP Sandbox Hell
UXP runs plugins in a heavily sandboxed environment. Nothing works like you expect:
No real file system access. You can’t read arbitrary files. You get a “file token” that expires and only works for specific operations. Want to cache brand assets locally? Too bad.
No external API calls. Fetch requests to external URLs are blocked by default. You need special permissions that users have to approve. Want to call Claude API directly? Nope.
No Node.js modules. The JavaScript runtime is browser-like but not actually a browser. No npm packages that depend on Node APIs. Want to use a decent HTTP client? Build it from scratch.
DOM limitations. The HTML renderer is custom, not WebKit or Chromium. CSS features randomly missing. Event handling is inconsistent. Want modern CSS Grid? Sometimes it works.
Security model is opaque. Permissions are all-or-nothing. Users see scary dialogs about “network access” and “file system access” without context. Approval rates tank.
The Workaround Architecture
To make v1 work, I built a proxy system:
// Plugin makes requests to local server
const response = await fetch('http://localhost:3001/api/brand/analyze', {
method: 'POST',
headers: { 'X-Plugin-Token': token },
body: documentData
});
// Local server proxies to real APIs
app.post('/api/brand/analyze', async (req, res) => {
const analysis = await claude.analyze(req.body);
res.json(analysis);
});
The plugin talks to a local FastAPI server, which handles real API calls, file system operations, and data persistence. Users install both the plugin AND a background service.
This worked, but barely.
User experience was terrible:
- Install plugin from Creative Cloud marketplace
- Download and run local service installer
- Configure network permissions
- Approve scary security dialogs
- Maybe the plugin works
What Broke the Model
Creative Cloud licensing. Adobe’s review process for plugins is unpredictable. Updates take weeks to approve. A single rejected update can block your entire product pipeline.
User education. Explaining why they need to install a background service was impossible. “It’s for security” doesn’t resonate with designers who just want brand feedback.
Platform dependency. UXP only works inside Adobe applications. What if users want brand analysis outside Photoshop? What if they’re designing in Figma?
Performance. Reading large PSD files through the UXP API is slow. Extracting design elements, fonts, and colors can take 30+ seconds for complex documents.
The v2 Architecture
Instead of fighting the sandbox, I built around it.
brand.ai v2 is a browser-based creative platform with optional Adobe integration:
Core platform: React web app with AI-powered brand tools. Upload files, get analysis, generate brand guidelines, manage design systems.
Adobe integration: Lightweight UXP plugin that exports documents to the web platform. No local services, no complex permissions, no proxy servers.
InDesign rendering pipeline: Instead of trying to parse Adobe files directly, generate output through the PDF pipeline I already built for neatworld.
// v2 plugin is tiny
async function exportToWeb() {
const token = await getExportToken();
const fileData = await app.activeDocument.exportFile(
ExportFormat.IDML_TYPE,
temporaryFile
);
// Upload to web platform, let it handle the analysis
window.open(`https://brand.ai/import?token=${token}&file=${fileData}`);
}
Why Browser-Based Wins
No installation friction. Users click a link, the app loads. No marketplace approval, no background services, no security dialogs.
Platform independence. Works with any design tool that can export common formats. Figma users, Canva users, even PowerPoint users can analyze their work.
Modern web stack. React, TypeScript, proper bundling, CSS Grid that actually works. Build tools that don’t fight you.
Flexible deployment. A/B testing, feature flags, instant updates. Ship fixes in minutes, not weeks.
Subscription model clarity. $149/month for the platform. Simple pricing, clear value proposition.
The InDesign Integration Pattern
Here’s the key insight that made v2 work: instead of trying to analyze Adobe files inside the plugin, export them and analyze on the web platform.
# Web platform receives IDML upload
@app.post("/brand/analyze")
async def analyze_design(file: UploadFile):
# Convert IDML to structured data
doc_data = parse_idml(file)
# Extract design elements
colors = extract_colors(doc_data)
fonts = extract_fonts(doc_data)
layout = extract_layout(doc_data)
# Run brand analysis with Claude
analysis = await claude_analyze(colors, fonts, layout, brand_guidelines)
# Generate feedback report
return {
"compliance_score": analysis.score,
"issues": analysis.issues,
"suggestions": analysis.suggestions,
"pdf_report": await generate_pdf_report(analysis)
}
The plugin becomes a bridge, not a standalone application. All the complex logic runs on the web platform where I control the environment.
Performance and UX Benefits
Faster analysis. Server-side processing with proper Python libraries instead of JavaScript polyfills in a sandbox.
Better feedback. Rich HTML reports with interactive elements, not limited UXP panel UI.
Collaborative features. Share analysis results with team members, compare against previous versions, build brand asset libraries.
Mobile access. Brand managers can review compliance reports on their phones. Try that with a Photoshop plugin.
The Tradeoff
Real-time feedback is gone. v1 could analyze your work as you designed. v2 requires an export step. That’s a workflow change some users won’t accept.
File format limitations. I can only analyze what I can parse. Complex Photoshop documents with custom effects might lose fidelity in export.
Network dependency. v1 worked offline (after the local service was running). v2 requires internet for every analysis.
Subscription friction. Desktop software feels “owned,” web platforms feel “rented.” Some users prefer the one-time purchase model of v1.
Current Status
brand.ai v2 is in beta with a handful of creative teams. The core analysis engine works well. The browser-based UI is significantly better than the UXP version.
But adoption is slower than expected. The export workflow breaks the creative flow more than I anticipated. Power users miss the real-time feedback from v1.
What I’d Do Next
Hybrid approach. Keep the lightweight UXP plugin for real-time feedback, but make it dependent on the web platform for complex analysis. Best of both worlds.
Better integrations. Figma plugin, Sketch plugin, Canva integration. Don’t be Adobe-only.
API-first architecture. Let other tools integrate brand analysis directly. Build the platform, let others build the interfaces.
The Lesson
Sometimes the constraints are the feature. UXP’s limitations forced me to build a better architecture—one that’s platform-independent, more maintainable, and easier to scale.
The sandbox wasn’t a bug, it was feedback. It told me that tightly coupling to Adobe’s ecosystem was the wrong bet.
v2 is a better product because it’s not a plugin at all. It’s a platform that happens to integrate with plugins.
When the tools fight you, build different tools.