Python-to-JavaScript Bridge for After Effects: How It Works
Python-to-JavaScript Bridge for After Effects: How It Works
The After Effects Automation toolkit lets you control After Effects from Python. But After Effects doesn’t speak Python natively — it speaks ExtendScript, a JavaScript dialect. So how does the bridge work?
In this post, I’ll explain the architecture.
The problem
After Effects exposes two main automation paths:
- ExtendScript (.jsx files): You can run scripts inside After Effects to manipulate comps, layers, and renders. But ExtendScript is old, limited, and not fun to write at scale.
- COM on Windows / AppleScript on macOS: These let external apps send commands to After Effects, but they’re platform-specific and verbose.
What developers actually want is a clean Python API:
project.comp("Main").layer("Title").text = "Hello World"
So the toolkit builds a bridge: Python on one side, ExtendScript/COM on the other.
The architecture
┌────────────────┐
│ Python API │
│ (your script) │
└───────┬────────┘
│ method calls
┌───────┴────────┐
│ Bridge layer │
│ (Python lib) │
└───────┬────────┘
│ generates JSX / COM commands
┌───────┴────────┐
│ After Effects │
│ (ExtendScript) │
└────────────────┘
Layer 1: The Python API
The top layer is designed to feel natural to Python developers. Objects map to After Effects concepts:
app = AfterEffectsApp()
project = app.project
comp = project.comp("Main")
layer = comp.layer("Logo")
layer.position = [960, 540]
layer.opacity = 80
Each object is a thin wrapper. It doesn’t hold much state itself — it just knows how to build commands.
Layer 2: The bridge
When you call a method, the bridge does one of two things:
Option A: Generate and execute JSX
For complex operations, the toolkit builds an ExtendScript file dynamically and tells After Effects to execute it:
# Python
layer.text = "New Title"
# Generates JSX like:
# var comp = app.project.itemByName("Main");
# var layer = comp.layer("Logo");
# layer.property("Source Text").setValue("New Title");
The JSX is written to a temp file and executed via app.doScript() or aerender.
Option B: Use COM/AppleScript for simple commands
For quick operations like opening a project or starting a render, the toolkit may use COM on Windows:
import win32com.client
ae = win32com.client.Dispatch("AfterEffects.Application")
ae.OpenProject("/path/to/project.aep")
On macOS, it uses osascript to send AppleScript commands.
Handling return values
One challenge with the bridge is getting data back. After Effects operations don’t always return structured data. The toolkit solves this by:
- Writing ExtendScript that serializes results to JSON
- Saving that JSON to a temp file
- Reading the file from Python
For example, to get a list of comps:
var result = [];
for (var i = 1; i <= app.project.numItems; i++) {
var item = app.project.item(i);
if (item instanceof CompItem) {
result.push({name: item.name, width: item.width, height: item.height});
}
}
var file = new File("/tmp/ae_comps.json");
file.open("w");
file.write(JSON.stringify(result));
file.close();
Python then reads /tmp/ae_comps.json and returns a list of dictionaries.
Error handling
After Effects errors come back as ExtendScript exceptions. The bridge catches these, parses the error message, and raises a Python exception with context:
try:
comp.layer("Missing Layer")
except LayerNotFoundError as e:
print(f"Layer not found: {e.layer_name}")
Why this matters
The bridge turns a painful automation workflow into something clean and testable. You get:
- Familiar syntax: Python instead of ExtendScript
- Reusability: Write functions, classes, and tests
- Integration: Combine After Effects with Pandas, requests, or any Python library
- Cross-platform support: One API that works on Windows and macOS
Want to explore the code?
The toolkit is open source: github.com/jhd3197/after-effects-automation
If you’re curious about the bridge implementation, the bridge/ directory is the best place to start.
Related posts: