// ISO_PrecompSelectedLayersToSeparateComps.jsx // Artist: https://www.jasonfletcher.info/ // Code generated by GPT-5.5 // Date: June 11, 2026 // Description: This After Effects script will look at the user-selected layers in the active comp and then proceed to precomp each layer into its own unique comp. It will then move all of the newly created precomps into a folder named "Precomped via script". All of the functionality of the native AE precomp tool is replicated within this script. Although interestingly it differs in that the "Leave all attributes in " can utilize the "Adjust composition duration to the time span of the selected layers" option. (function () { var SETTINGS_SECTION = "ISO_PrecompSelectedLayersToSeparateComps"; var KEY_MOVE_ALL_ATTRS = "MoveAllAttributes"; var KEY_ADJUST_DURATION = "AdjustDuration"; function readSetting(section, key, defaultValue) { try { if (app.settings.haveSetting(section, key)) { return app.settings.getSetting(section, key); } } catch (err) { } return defaultValue; } function writeSetting(section, key, value) { try { app.settings.saveSetting(section, key, String(value)); } catch (err) { } } function loadPrefs() { var prefs = { moveAllAttributes: true, adjustDuration: true }; prefs.moveAllAttributes = ( readSetting( SETTINGS_SECTION, KEY_MOVE_ALL_ATTRS, "1" ) === "1" ); prefs.adjustDuration = ( readSetting( SETTINGS_SECTION, KEY_ADJUST_DURATION, "1" ) === "1" ); return prefs; } function savePrefs(prefs) { writeSetting( SETTINGS_SECTION, KEY_MOVE_ALL_ATTRS, prefs.moveAllAttributes ? "1" : "0" ); writeSetting( SETTINGS_SECTION, KEY_ADJUST_DURATION, prefs.adjustDuration ? "1" : "0" ); } function buildDialog(activeCompName, prefs) { var w = new Window("dialog", "Pre-compose Layers to Separate Comps"); w.orientation = "column"; w.alignChildren = ["fill", "top"]; w.margins = 16; w.spacing = 12; var modePanel = w.add("panel", undefined, "Pre-compose mode"); modePanel.orientation = "column"; modePanel.alignChildren = ["left", "top"]; modePanel.margins = 12; modePanel.spacing = 8; var rbLeaveAttrs = modePanel.add( "radiobutton", undefined, 'Leave all attributes in "' + activeCompName + '"' ); var rbMoveAttrs = modePanel.add( "radiobutton", undefined, "Pre-compose: move all attributes into the new composition" ); rbMoveAttrs.value = prefs.moveAllAttributes; rbLeaveAttrs.value = !prefs.moveAllAttributes; var durationCheck = w.add( "checkbox", undefined, "Adjust composition duration to the time span of the selected layers" ); durationCheck.value = prefs.adjustDuration; var buttonGroup = w.add("group"); buttonGroup.orientation = "row"; buttonGroup.alignment = ["right", "center"]; buttonGroup.spacing = 8; var cancelBtn = buttonGroup.add( "button", undefined, "Cancel", { name: "cancel" } ); var okBtn = buttonGroup.add( "button", undefined, "OK", { name: "ok" } ); var result = { ok: false, moveAllAttributes: true, adjustDuration: true }; okBtn.onClick = function () { result.ok = true; result.moveAllAttributes = rbMoveAttrs.value; result.adjustDuration = durationCheck.value; w.close(1); }; cancelBtn.onClick = function () { result.ok = false; w.close(0); }; if (w.show() !== 1) { return null; } if (!result.ok) { return null; } return result; } function getTargetFolder() { var i; var item; var targetFolder = null; for (i = 1; i <= app.project.numItems; i++) { item = app.project.item(i); if ( item instanceof FolderItem && item.name === "Precomped via script" ) { targetFolder = item; break; } } if (targetFolder === null) { targetFolder = app.project.items.addFolder( "Precomped via script" ); } return targetFolder; } function adjustPrecompDuration( precompItem, sourceInPoint, sourceOutPoint ) { var duration = sourceOutPoint - sourceInPoint; var layer; var i; if (duration <= 0) { duration = 0.001; } precompItem.duration = duration; precompItem.workAreaStart = 0; precompItem.workAreaDuration = duration; if (precompItem.numLayers > 0) { for (i = 1; i <= precompItem.numLayers; i++) { layer = precompItem.layer(i); try { layer.startTime = layer.startTime - sourceInPoint; } catch (err) { } } } } function main() { var activeItem = app.project.activeItem; var selectedLayers; var prefs; var dialogResult; var targetFolder; var layersToProcess = []; var i; var precompCount = 0; if (!(activeItem instanceof CompItem)) { alert( "Please open a composition and select one or more layers." ); return; } selectedLayers = activeItem.selectedLayers; if ( !selectedLayers || selectedLayers.length === 0 ) { alert( "Please select one or more layers in the active composition." ); return; } prefs = loadPrefs(); dialogResult = buildDialog( activeItem.name, prefs ); if (dialogResult === null) { return; } targetFolder = getTargetFolder(); for (i = 0; i < selectedLayers.length; i++) { layersToProcess.push({ index: selectedLayers[i].index, name: selectedLayers[i].name, inPoint: selectedLayers[i].inPoint, outPoint: selectedLayers[i].outPoint }); } layersToProcess.sort(function (a, b) { return b.index - a.index; }); app.beginUndoGroup( "Precomp Selected Layers" ); try { for ( i = 0; i < layersToProcess.length; i++ ) { var layerInfo = layersToProcess[i]; var precompName = layerInfo.name + "_PRECOMP"; var precompItem = activeItem.layers.precompose( [layerInfo.index], precompName, dialogResult.moveAllAttributes ); if (precompItem !== null) { precompItem.parentFolder = targetFolder; if ( dialogResult.adjustDuration ) { adjustPrecompDuration( precompItem, layerInfo.inPoint, layerInfo.outPoint ); } precompCount++; } } savePrefs(dialogResult); alert( "Layers which were precomped: " + precompCount ); } catch (err) { alert( "An error occurred:\n\n" + err.toString() ); } finally { app.endUndoGroup(); } } main(); })();