Developing Adobe CEP Extensions for After Effects – Technical Guide¶
Table of Contents¶
- Development Setup
- Prerequisites
- Folder Structure
- manifest.xml Configuration
- Installation & Configuration
- Debugging CEP Extensions
- Client-Side Debugging (Panel UI)
- Server-Side Debugging (ExtendScript)
- Troubleshooting Debugging
- Best Practices for Efficient CEP Development
- Minimize Host Round-Trips
- Optimize ExtendScript Execution
- Efficient UI Updates
- Common Pitfalls (and Solutions)
- Performance Optimizations Summary
- Security Considerations
- Hyperbrew BOLT-CEP Template
- Overview & Features
- Installation & Usage Guide
- Real-World Examples
- Common Issues and How to Resolve Them
- Alternative CEP Development Frameworks & Tools
- Battle Axe's Brutalism (with Bombino)
- CEP + Parcel (cep-bundler / fusepilot)
- Yeoman Generators (CEP)
- CEP Extension Toolkit / Legacy Methods
- Other Tools
- Advanced Topics
- Automation & CI/CD for CEP Extensions
- Distributing & Updating CEP Extensions
- UXP vs. CEP (Future-Proofing Your Extensions)
Note: This guide complements the existing documentation in the
/docs/guidedirectory. For specific topics, refer to: - Getting Started Guide - Basic setup and first extension - Debugging Guide - Detailed debugging workflows - Network Requests - Making HTTP requests from CEP - Exporting Files - File I/O operations
1. Development Setup¶
Prerequisites¶
To develop Adobe CEP extensions for After Effects, ensure you have the following:
-
Adobe After Effects (CC 2014 or later) – CEP (Common Extensibility Platform) panels are supported in After Effects CC 2014 and above. After Effects 2022/2023 use CEP 11 (Chromium 88, Node 15) under the hood, and both Windows and macOS (Intel & Apple Silicon) are supported (on Apple Silicon, CEP panels run natively in AE).
-
Enable Debug Mode – During development, allow unsigned extensions to load. Set the Player Debug Mode flag:
- Windows: Add a reg key
PlayerDebugMode=1underHKEY_CURRENT_USER/Software/Adobe/CSXS.<version>(e.g. CSXS.10 for CEP 10) - macOS: Run:
defaults write com.adobe.CSXS.<version> PlayerDebugMode 1
Note: Use the CEP version matching your AE release (AE 2022+ uses CSXS.11). After enabling, restart After Effects. On macOS, you may need to kill the
cfprefsdprocess or log out/in for the change to take effect.
For more details on debug mode setup, see our Debugging Guide.
-
Node.js and NPM/Yarn – Install Node.js (v16+ recommended) if you plan to use modern toolchains or frameworks (React, Vue, TypeScript, etc.). Yarn Classic (v1.x) is often required by certain CEP build tools (e.g. BOLT-CEP).
-
Code Editor / IDE – Use an editor like Visual Studio Code for coding HTML/CSS/JS and ExtendScript. Install the ExtendScript Debugger extension for VSCode to debug host scripts (ExtendScript) within After Effects.
-
CEP Resources – Download the Adobe CEP SDK/Resources which include the CSInterface.js library and sample code. The CSInterface JavaScript library simplifies communication between the panel (JS) and After Effects (ExtendScript). In many setups, this is included or bundled, but be aware of it for manual projects.
-
After Effects Scripting Permissions – In After Effects, enable "Allow Scripts to Write Files and Access Network" (Under Edit > Preferences > Scripting & Expressions). This allows your extension's scripts to access the file system and network if needed.
For a more detailed setup walkthrough, see our Getting Started Guide.
Folder Structure¶
A CEP extension is a folder containing your panel's files and a manifest. The minimum required structure is: a CSXS folder with a manifest.xml, and your HTML/JS/CSS and ExtendScript code files. A recommended structure is to separate "client" (panel UI) and "host" (ExtendScript) code:
my-extension/
├── CSXS/
│ └── manifest.xml
├── client/
│ ├── index.html
│ ├── style.css
│ ├── index.js
│ └── lib/
│ └── CSInterface.js
└── host/
└── index.jsx
-
CSXS/manifest.xml– Extension manifest (XML format) defining the extension's identity, supported host apps/versions, and panel settings. (This file is required; without it, After Effects will not recognize your extension.) -
client/– Front-end assets (HTML, CSS, JS). For example:index.html,style.css,index.js, and theCSInterface.jslibrary. This is the code that runs in a Chromium Embedded browser inside After Effects. -
host/– Host scripts (ExtendScript.jsxfiles) that run in After Effects. For example:index.jsxcontaining ExtendScript code to be invoked for AE-specific functionality.
This structure cleanly separates UI code from scripting logic. However, the structure is flexible as long as the manifest paths are configured correctly. You may choose a different layout (some frameworks bundle everything under one dist folder), but ensure the manifest's references to files are updated accordingly.
For more details on file organization and best practices, see our Getting Started Guide.
manifest.xml Configuration¶
The CSXS/manifest.xml is the heart of the extension configuration. At minimum, it must specify:
- An Extension Bundle ID
- An Extension ID
- Target Host Application(s) with version range
- Required CEP runtime version
- Entry points (HTML and ExtendScript files)
Key fields in manifest.xml for an After Effects CEP panel include:
-
ExtensionBundleId – A unique reverse-domain identifier for your extension bundle (e.g.
com.mycompany.myextension). -
Extension Id – A unique ID for the specific extension (panel). Often this is
ExtensionBundleId.panel(e.g.com.mycompany.myextension.panel). This ID will appear twice (in ExtensionList and later in the<Extension Id="...">node). -
Version – Your extension version (e.g.
1.0.0). Keep this in sync with your releases. -
HostList – The Adobe apps that will host this extension. Use the host's code and a version range. For After Effects, the host code is
This indicates the panel is compatible with AE 16.0 and up (16.0 was CC 2019). Using a range or"AEFT". For example:"*", you can future-proof the manifest so the panel doesn't disappear when AE is updated. -
RequiredRuntime – Indicates the CEP runtime version required. For CEP 11 (used in AE 2022+), use:
For broader compatibility you might use a slightly lower version (CEP 9 or 10), but ensure it's supported by your code. -
MainPath – The relative path to your panel's HTML file (e.g.
client/index.html). This file is loaded in the panel UI. -
ScriptPath – The relative path to your ExtendScript file (e.g.
host/index.jsx). This will be automatically loaded into After Effects when the panel opens. -
CEFCommandLine (optional) – Command-line switches for the embedded Chromium engine. For instance, you can enable Node.js, or allow remote debugging. By default, Node is enabled in CEP panels.
-
Extension UI Settings – Within
<UI>, configure how your panel appears: <Menu>My Extension</Menu>(name in the Window > Extensions menu)<Geometry>(panel default width/height)<Icons>(icon path)<Language>etc. For a panel,Type="Panel"is typical. Use<AutoVisible>true</AutoVisible>if you want the panel to auto-open when AE starts.
Below is a simplified example of a manifest for an After Effects panel:
<?xml version="1.0" encoding="UTF-8"?>
<ExtensionManifest ExtensionBundleId="com.mycompany.myextension" ExtensionBundleVersion="1.0.0" Version="11.0">
<ExtensionList>
<Extension Id="com.mycompany.myextension.panel" Version="1.0.0" />
</ExtensionList>
<ExecutionEnvironment>
<HostList>
<Host Name="AEFT" Version="[16.0,99.0]"/>
</HostList>
<LocaleList>
<Locale Code="All"/>
</LocaleList>
<RequiredRuntimeList>
<RequiredRuntime Name="CSXS" Version="11.0"/>
</RequiredRuntimeList>
</ExecutionEnvironment>
<DispatchInfoList>
<DispatchInfo>
<UI>
<Type>Panel</Type>
<Menu>My Extension</Menu>
<Geometry>
<Size Width="300" Height="200"/>
</Geometry>
</UI>
<Resources>
<MainPath>client/index.html</MainPath>
<ScriptPath>host/index.jsx</ScriptPath>
</Resources>
</DispatchInfo>
</DispatchInfoList>
</ExtensionManifest>
This manifest would register a panel named "My Extension" under After Effects' Window > Extensions menu. It targets AE CC 2019 and later, requires CEP 11, and loads client/index.html with its ExtendScript in host/index.jsx.
For more detailed manifest options and examples, see our Getting Started Guide.
Installation & Configuration¶
During development, you can load your extension by placing its folder in the appropriate extensions directory. The locations are:
- macOS:
- User-level:
~/Library/Application Support/Adobe/CEP/extensions -
System-wide:
/Library/Application Support/Adobe/CEP/extensions -
Windows:
- User-level:
%USERPROFILE%\AppData\Roaming\Adobe\CEP\extensions - System-wide:
%ProgramFiles(x86)%\Common Files\Adobe\CEP\extensions
For development, the user-level path is recommended (no admin rights needed). Create the CEP/extensions folder if it doesn't exist. Copy your extension folder (containing the CSXS subfolder) into this directory. After Effects will scan this on launch to populate the Extensions menu.
Note: For more details on installation paths and troubleshooting, see our Getting Started Guide.
Debug mode: As noted in Prerequisites, ensure you've enabled PlayerDebugMode=1 on your system. If not, After Effects will refuse to load your extension with an error about it not being properly signed. When debug mode is on, you can ignore signature warnings during development.
Now launch After Effects. You should find your extension under Window > Extensions (in newer versions it might be under "Extensions (Legacy)" since CEP is the legacy platform). Click your extension's name to open the panel. If everything is configured correctly, a panel window will appear (it might be blank if you haven't added UI yet).
If you don't see your extension in the menu, double-check the following common issues:
-
The
manifest.xmlis in aCSXSfolder at the root of your extension folder. The extension folder name can be anything, but it should match what you placed in the extensions directory. -
The manifest's Host entry for After Effects covers your AE version. For example, if your manifest only has
Version="16.0"and you're on After Effects 2023 (version 23.x), it will not show up. Using a range like[16.0,99.0]or"*"ensures it includes newer versions. -
PlayerDebugMode is truly set (on macOS, remember to restart or kill prefs cache as mentioned). If not set, AE will list the extension but show an error on opening, or it may not list it at all if unsigned.
-
The extension is in the correct directory. On Windows, note the distinction between
%AppData%(user Roaming) vs.%ProgramFiles(x86)%(system). On macOS, ensure it's in your user Library if you didn't install system-wide.
For detailed troubleshooting steps and common issues, see our Debugging Guide.
2. Debugging CEP Extensions¶
During development, robust debugging is critical. You'll typically debug two contexts: - Client-side: The panel's HTML/JS running in Chrome Embedded Framework - Server-side: The ExtendScript running in After Effects
For comprehensive debugging workflows, see our dedicated Debugging Guide. Here's a quick overview of the key concepts:
Client-Side Debugging (Panel UI)¶
CEP panels run on an embedded Chromium browser engine, so you can use Chrome DevTools to debug HTML, CSS, and JavaScript. To set this up:
- Create a
.debugfile in your extension root with your panel's debug configuration - Enable remote debugging in Chrome
- Use Chrome DevTools for live debugging, console logs, and network monitoring
For step-by-step instructions and troubleshooting, see Client Side Debugging.
Server-Side Debugging (ExtendScript)¶
For debugging the ExtendScript (JSX) that runs in After Effects:
- Install the Adobe ExtendScript Debugger extension in VS Code
- Set up a launch configuration for After Effects
- Use breakpoints and step-through debugging in your JSX files
For detailed ExtendScript debugging setup, see Server Side Debugging.
Troubleshooting Debugging¶
Common debugging issues include: - Panel UI not loading (gray panel) - DevTools connection problems - ExtendScript debugger attachment issues
See our Troubleshooting Guide for solutions to these and other debugging challenges.
3. Best Practices for Efficient CEP Development¶
Developing CEP extensions for After Effects involves both web development and scripting. Here are best practices and common pitfalls to keep your extension efficient, stable, and secure.
Minimize Host Round-Trips¶
One of the biggest performance considerations is the communication between the panel (JS) and After Effects (ExtendScript). Each call to CSInterface.evalScript() invokes After Effects to run some ExtendScript and possibly return a result. These calls are relatively slow (millisecond-scale) and can block the UI.
Important:
CSInterface.evalScript()is essentially synchronous – it will block the JavaScript thread until the ExtendScript finishes (even if you use a callback, the UI thread in CEP is halted while the host app runs the script).
To reduce the number of evalScript calls:
-
Batch operations: If you need several pieces of data from AE, retrieve them in one JavaScript call:
-
Avoid tight loops calling ExtendScript: If you have a loop in JS that calls an ExtendScript function each iteration, that will be very slow. Instead:
-
Use Callbacks or Promises for asynchronous flows:
// Using callback CSInterface.evalScript('getProjectData()', result => { // Handle result when ready }) // Using Promise wrapper function evalScriptPromise(script) { return new Promise((resolve, reject) => { CSInterface.evalScript(script, result => { if (result === 'EvalScript error') reject(new Error(result)) else resolve(result) }) }) }
For more examples of efficient communication patterns, see our Network Requests Guide.
Optimize ExtendScript Execution¶
Long-running ExtendScript code will freeze After Effects (and by extension, your panel) until it completes. To optimize:
-
Use
app.scheduleTask()for long operations:// Process items in chunks function processChunk(items, startIndex, chunkSize) { for (var i = startIndex; i < startIndex + chunkSize && i < items.length; i++) { // Process item } if (startIndex + chunkSize < items.length) { app.scheduleTask('processChunk(items, ' + (startIndex + chunkSize) + ', ' + chunkSize + ')', 100) } } -
Leverage AE's native functions whenever possible:
-
Use persistent ExtendScript engine for caching:
For more ExtendScript optimization techniques, see our Getting Started Guide.
Efficient UI Updates¶
The panel's UI is essentially a webpage. Follow these best practices:
-
Throttle frequent updates:
-
Virtual DOM / Frameworks: If using React, Vue, or Svelte (via BOLT-CEP or other frameworks), leverage their optimizations.
-
Caching in UI:
-
Idle timing for updates:
For more UI optimization techniques, see our Getting Started Guide.
Common Pitfalls (and Solutions)¶
Here are common issues you might encounter and how to solve them:
-
Extension not appearing in menu:
See Getting Started Guide for detailed troubleshooting.Problem: Extension doesn't show up in Window > Extensions menu Solution: Check manifest version range and installation path -
"Extension could not be loaded because it was not properly signed" error:
See Debugging Guide for details.Problem: Extension fails to load with signing error Solution: Enable PlayerDebugMode=1 for development For distribution: Sign the extension with a certificate -
appor other AE objects undefined in JS: -
Scope and lifetime of ExtendScript code:
// In JSX file #targetengine "main" // Variables persist in engine while it lives var persistentData = {} // But are gone if AE closes/reloads // Solution: Re-initialize on panel launch function initializeData() { if (!persistentData.initialized) { persistentData = { initialized: true, /* ... */ } } } -
Cross-platform issues:
For more troubleshooting tips, see our Debugging Guide.
Performance Optimizations Summary¶
Key points for maintaining good performance:
- Minimize JS-ExtendScript Communication:
- Batch operations into fewer
evalScriptcalls - Cache results when possible
-
Use asynchronous patterns for better UI responsiveness
-
Optimize ExtendScript:
- Break up long operations with
app.scheduleTask - Use native AE functions over custom implementations
-
Cache expensive computations in persistent engine
-
Efficient UI Updates:
- Throttle frequent updates
- Use modern frameworks' optimizations
- Implement smart caching
- Use idle callbacks for background tasks
For detailed performance tips, see our Network Requests Guide.
Security Considerations¶
CEP extensions run with high privileges on a user's machine. Follow these security practices:
-
Code Signing:
-
HTTPS and Network Security:
// Always use HTTPS for external requests fetch('https://api.example.com/data', { // ... options }) // Handle network errors try { const response = await fetch(url) if (!response.ok) throw new Error(`HTTP error: ${response.status}`) } catch (error) { console.error('Network request failed:', error) } -
Content Security Policy:
-
Node.js Security:
-
Prevent Script Injection:
For more security best practices, see our Network Requests Guide.
4. Hyperbrew BOLT-CEP Template¶
BOLT-CEP is a modern boilerplate for building CEP extensions, aimed at making development faster and easier. It integrates a robust build system with support for popular front-end frameworks and streamlines the CEP setup.
Overview & Features¶
-
Lightning-Fast HMR (Hot Module Replacement):
Your panel will live-reload instantly as you save changes, without needing to manually refresh or reload the extension. -
Multi-Framework UI Support:
-
Modern JavaScript in Both Layers:
-
Optimized Bundling:
Installation & Usage Guide¶
-
Create a New Project:
-
Install Dependencies:
-
Configure Your Extension:
-
Development Workflow:
-
Building for Production:
For more details on project setup, see our Getting Started Guide.
Real-World Examples¶
BOLT-CEP is used in production by various tools:
-
Battle Axe's Rubberhose 3:
// Example of BOLT-CEP with React and TypeScript import { useEffect, useState } from 'react' import { evalTS } from '@bolt-cep/core' function RiggingPanel() { const [bones, setBones] = useState([]) useEffect(() => { evalTS('getBones()').then(setBones) }, []) return ( <div className="panel"> {bones.map(bone => ( <BoneControl key={bone.id} data={bone} /> ))} </div> ) } -
Klutz GPT:
// Example of BOLT-CEP with Vue <template> <div class="ai-panel"> <prompt-input @submit="generateAnimation" /> <results-view :animations="results" /> </div> </template> <script setup> import { ref } from 'vue' import { evalTS } from '@bolt-cep/core' const results = ref([]) async function generateAnimation(prompt) { const animation = await evalTS('createFromPrompt', { prompt }) results.value.push(animation) } </script>
Common Issues and How to Resolve Them¶
-
Apple Silicon Build Issues:
-
Yarn vs npm:
-
Multiple Adobe Apps:
For more troubleshooting tips, see our Debugging Guide.
5. Alternative CEP Development Frameworks & Tools¶
While BOLT-CEP is a powerful all-in-one solution, there are other frameworks and tools to consider for CEP extension development. Each has its own philosophy and tooling.
Battle Axe's Brutalism (with Bombino)¶
Brutalism is a component-based UI framework specifically designed for Adobe panels. It provides pre-styled components that mimic Adobe's UI.
Key Features:
// Example Brutalism component usage
<template>
<div class="panel">
<Button @click="doThing">Action</Button>
<Dropzone @drop="handleDrop" />
<ColorPicker v-model="color" />
</div>
</template>
<script>
import { Button, Dropzone, ColorPicker } from 'brutalism'
export default {
components: { Button, Dropzone, ColorPicker },
data: () => ({
color: '#FF0000'
}),
methods: {
doThing() {
// Automatically handles theme/scaling
}
}
}
</script>
Comparison to Bolt: - Brutalism is a UI framework, Bolt is a build system - Brutalism assumes Vue.js usage - Can be used together (Bolt for build, Brutalism for UI)
CEP + Parcel (cep-bundler / fusepilot)¶
For a simpler build setup, some tools use Parcel bundler:
# Using cep-bundler
npm install @adobe-extension-tools/cep-bundler
# Example config
{
"name": "my-extension",
"version": "1.0.0",
"scripts": {
"build": "cep-bundler build",
"dev": "cep-bundler watch"
}
}
Key Features: - Zero configuration bundling - Automatic dependency handling - Live reload support
Comparison to Bolt: - Simpler setup but fewer features - Manual manifest management - No integrated TypeScript for JSX
Yeoman Generators (CEP)¶
Older but still functional scaffolding tools:
# Install Yeoman and a CEP generator
npm install -g yo generator-cep-vue-cli
# Generate a new extension
yo cep-vue-cli
# Structure created:
my-extension/
├── CSXS/
├── src/
│ ├── components/
│ └── main.js
└── jsx/
└── main.jsx
Comparison to Bolt: - Older build tools (Webpack 4, etc.) - Basic live-reload (no HMR) - Manual debug setup required
CEP Extension Toolkit / Legacy Methods¶
The traditional way of building CEP extensions:
<!-- Direct HTML/JS approach -->
<!DOCTYPE html>
<html>
<head>
<script src="CSInterface.js"></script>
<script src="jquery.js"></script>
</head>
<body>
<button onclick="doThing()">Click Me</button>
<script>
function doThing() {
var csInterface = new CSInterface()
csInterface.evalScript('app.project.activeItem.name')
}
</script>
</body>
</html>
Comparison to Modern Tools: - No build step (direct browser loading) - Manual dependency management - No hot reload or modern JS features
Other Tools¶
-
CEP-Sparker:
-
Bombino (Standalone):
-
Adobe UXP (Future):
For a comparison of features across frameworks:
| Feature | BOLT-CEP | Brutalism | Parcel | Yeoman |
|---|---|---|---|---|
| Hot Reload | ✅ HMR | ✅ Basic | ✅ Basic | ❌ |
| TypeScript | ✅ Full | ✅ Vue | ❌ | ❌ |
| UI Components | ❌ | ✅ Adobe-style | ❌ | ❌ |
| Framework Choice | ✅ Multiple | Vue only | ✅ Any | Generator specific |
| Build Tools | Vite | Vue CLI | Parcel | Varies |
| Debug Setup | ✅ Automatic | Manual | Manual | Manual |
For more details on choosing a framework, see our Getting Started Guide.
6. Advanced Topics¶
Automation & CI/CD for CEP Extensions¶
Automate your build and packaging process using continuous integration:
-
Scripting the Build:
-
GitHub Actions Workflow:
# .github/workflows/release.yml name: Release on: release: types: [created] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 - name: Install dependencies run: yarn install - name: Build extension run: yarn build - name: Sign ZXP run: | echo "${{ secrets.CERTIFICATE_B64 }}" | base64 -d > cert.p12 yarn sign env: CERT_PASSWORD: ${{ secrets.CERT_PASSWORD }} - name: Upload artifact uses: actions/upload-release-asset@v1 with: upload_url: ${{ github.event.release.upload_url }} asset_path: ./dist/MyExtension.zxp asset_name: MyExtension.zxp asset_content_type: application/octet-stream -
Automated Testing:
// Example ExtendScript test #target aftereffects function testCreateComp() { var comp = app.project.items.addComp('Test', 1920, 1080, 1, 10, 30) if (!comp) throw new Error('Failed to create comp') return 'Pass' } // Run tests try { testCreateComp() $.write('Tests passed\n') } catch (e) { $.write('Test failed: ' + e.message + '\n') }
Distributing & Updating CEP Extensions¶
Multiple distribution options are available:
-
Adobe Exchange:
-
Self-hosted Distribution:
// In your panel, check for updates async function checkForUpdates() { const response = await fetch('https://example.com/updates/check.json') const { version } = await response.json() if (version > currentVersion) { // Show update notification const updateURL = `https://example.com/download/${version}/MyExtension.zxp` CSInterface.openURLInDefaultBrowser(updateURL) } } -
Enterprise Distribution:
UXP vs. CEP (Future-Proofing Your Extensions)¶
Adobe is transitioning to UXP (Unified Extensibility Platform) for future extensions. Here's how to prepare:
-
Architectural Considerations:
-
Code Organization for Future Migration:
// Separate business logic from platform-specific code // platform/cep.ts export async function getActiveItemName(): Promise<string> { return new Promise(resolve => { const csInterface = new CSInterface() csInterface.evalScript('app.project.activeItem.name', resolve) }) } // platform/uxp.ts export async function getActiveItemName(): Promise<string> { const { app } = await import('uxp') return app.activeDocument.name } // Use in your components import { getActiveItemName } from './platform/cep' // or './platform/uxp' -
Feature Parity Considerations:
-
Gradual Migration Strategy:
// config.ts export const isUXP = Boolean(globalThis.require?.('uxp')) // Wrapper for platform-specific features export class PlatformBridge { static async getActiveItem() { if (isUXP) { const { app } = await import('uxp') return app.activeDocument } else { return new Promise(resolve => { const csInterface = new CSInterface() csInterface.evalScript('app.project.activeItem', resolve) }) } } }
For more details on UXP migration, see Adobe's UXP Documentation.
This concludes our comprehensive guide to developing Adobe CEP Extensions. For specific topics, refer to our other guides: - Getting Started Guide - Basic setup and first extension - Debugging Guide - Detailed debugging workflows - Network Requests - Making HTTP requests from CEP - Exporting Files - File I/O operations
For the latest updates and community support, visit the Adobe CEP Resources repository.