Motion designers often face the challenge of rendering individual layers from a composition for client reviews, asset delivery, or pipeline integration. Manually duplicating comps, soloing layers, and setting render queue outputs can be tedious and error-prone. The script you’ve shared elegantly solves this problem by automating the entire process. Let’s break down how it works, why it’s useful, and how you can integrate it into your workflow.
🎬 What This Script Does
This After Effects script automates the process of splitting selected layers into individual compositions and adding them to the render queue. Here’s the workflow it creates for you:
- Composition validation: Ensures you’re working with a valid comp and selected layers.
- Safe naming: Cleans up layer names by removing forbidden characters (
\ / : * ? " < > | %) to prevent file system errors. - Work area trimming: Sets the comp’s work area to match the layer’s in/out points, ensuring precise renders.
- Soloing layers: Automatically solos the target layer so only its content is rendered.
- Render queue automation: Adds each new comp to the render queue with correct time spans.
- Output file naming: Builds safe, unique filenames with layer indices and optional sequence markers (
[#####]). - Cross-platform path handling: Detects Windows vs macOS path separators to avoid broken file paths.
🎨 Benefits for Motion Designers
- Time savings: No more duplicating comps and soloing layers manually.
- Error prevention: Sanitized names and safe paths reduce crashes and broken renders.
- Client-ready exports: Quickly generate individual layer renders for feedback or delivery.
- Pipeline integration: Ideal for studios where assets need to be batch-rendered for compositing or editing.
- Consistency: Automated naming conventions keep your project organized.
🛠️ How to Use the Script
- Select layers: In your active composition, select the layers you want to render individually.
- Run the script: Execute the script from the After Effects Scripts panel.
- Choose output folder: A dialog will prompt you to select where the renders should be saved.
- Sit back: The script duplicates comps, solos layers, sets work areas, and adds everything to the render queue automatically.
- Render: Hit “Render” in the queue and enjoy perfectly split layer outputs.
💡 Practical Use Cases
- Client reviews: Send individual layer renders for approval without exporting the entire comp.
- Asset delivery: Provide editors or compositors with isolated elements.
- Motion design pipelines: Automate repetitive tasks in collaborative environments.
- Debugging: Render single layers to troubleshoot animation or effects issues.
⚡ Pro Tips
- Keep layer names short and descriptive—this script trims names to 50 characters for safety.
- If you’re rendering sequences, the script automatically adds
[#####]placeholders for frame numbering. - Use the generated “Render Queue Comps” folder to keep your project organized.
🎯 Conclusion
For motion designers, efficiency and precision are everything. This script transforms a repetitive, error-prone workflow into a streamlined process. Whether you’re delivering assets to clients, preparing elements for compositing, or simply organizing your renders, this automation will save you hours of manual work and keep your projects clean.
(function() {
// --- Helper: Safe Trim (Compatible with all AE versions) ---
function safeTrim(str) {
if (!str) return "";
return String(str).replace(/^\s+|\s+$/g, '');
}
// --- Helper: Clean Layer Name ---
function sanitizeName(name) {
// ERROR FIX: Ensure input is a string to prevent object errors
var str = String(name);
// ERROR FIX: Use 'new RegExp' to avoid parser crashing on forward slashes
// This targets: \ / : * ? " < > | and %
var forbidden = new RegExp("[\\\\\\/\\:\\*\\?\"<>|%]", "g");
var cleaned = str.replace(forbidden, '_');
return safeTrim(cleaned);
}
// --- Helper: Build Safe Path ---
function buildSafePath(folderObj, fileName) {
var separator = ($.os.indexOf("Windows") !== -1) ? "\\" : "/";
var cleanFolder = folderObj.fsName;
// Remove trailing slash if present
if (cleanFolder.slice(-1) === separator) {
cleanFolder = cleanFolder.slice(0, -1);
}
return cleanFolder + separator + fileName;
}
function main() {
var comp = app.project.activeItem;
// Check 1: Composition Validation
if (!comp || (comp.typeName !== "Composition" && !(comp instanceof CompItem))) {
alert("Please select a composition.");
return;
}
var selectedLayers = comp.selectedLayers;
if (selectedLayers.length === 0) {
alert("Please select at least one layer.");
return;
}
// Check 2: Output Folder
var outputFolder = Folder.selectDialog("Select the folder where you want to save the renders");
if (!outputFolder) return;
app.beginUndoGroup("Add Split Layers to Render Queue");
// Keep track of new items to clean up if critical error occurs
var createdComps = [];
try {
var projectFolder = app.project.items.addFolder("Render Queue Comps");
var layersAddedCount = 0;
for (var i = 0; i < selectedLayers.length; i++) {
var layer = selectedLayers[i];
var newComp = null;
try {
// 1. Duplicate Comp
// Validate layer name before duplication
var safeLayerName = (layer.name) ? sanitizeName(layer.name) : "Untitled_Layer";
newComp = comp.duplicate();
newComp.name = safeLayerName;
newComp.parentFolder = projectFolder;
createdComps.push(newComp); // Track for potential cleanup
// 2. Set Work Area
newComp.workAreaStart = layer.inPoint;
var duration = layer.outPoint - layer.inPoint;
// Ensure duration is at least 1 frame
if (duration < newComp.frameDuration) duration = newComp.frameDuration;
newComp.workAreaDuration = duration;
// 3. Solo the specific layer
var targetLayer = newComp.layer(layer.index);
targetLayer.solo = true;
targetLayer.enabled = true;
// Check if audio property exists before setting
if (targetLayer.hasAudio) targetLayer.audioEnabled = true;
// 4. Add to Render Queue
var rqItem = app.project.renderQueue.items.add(newComp);
if (!rqItem) continue;
rqItem.timeSpanStart = newComp.workAreaStart;
rqItem.timeSpanDuration = newComp.workAreaDuration;
// 5. Output Module & Extension Logic
var om = rqItem.outputModule(1);
var extension = ".mov";
var isSequence = false;
// Attempt to detect settings from the default template
if (om.file) {
var autoName = String(om.file.name);
var lastDot = autoName.lastIndexOf(".");
if (lastDot !== -1) {
extension = autoName.substring(lastDot);
}
if (autoName.indexOf("[") !== -1 || autoName.indexOf("#") !== -1) {
isSequence = true;
}
}
// 6. Final Filename Construction
var layerIndex = layer.index;
var suffix = (layerIndex < 10) ? "_0" + layerIndex : "_" + layerIndex;
// Re-sanitize to be absolutely safe
var cleanName = sanitizeName(layer.name);
if (cleanName.length > 50) cleanName = cleanName.substring(0, 50);
var finalFileName;
if (isSequence) {
finalFileName = cleanName + suffix + "_[#####]" + extension;
} else {
finalFileName = cleanName + suffix + extension;
}
// 7. Assign File
var fullPath = buildSafePath(outputFolder, finalFileName);
om.file = new File(fullPath);
layersAddedCount++;
} catch (innerErr) {
// If this specific layer failed, remove the temp comp
if (newComp) newComp.remove();
alert("Warning: Skipped layer '" + layer.name + "' due to error:\n" + innerErr.message);
}
}
alert("Process Complete.\nAdded " + layersAddedCount + " layers to Render Queue.");
} catch (err) {
alert("Critical Error:\n" + err.message);
} finally {
app.endUndoGroup();
}
}
main();
})();