// ISO_RandomizePositionWithinRange.jsx // Artist: https://www.jasonfletcher.info/ // Code generated by ChatGPT-5.5 // Date: July 16, 2026 // Description: This After Effects script will randomize the X and Y Position of the selected layers within user-defined ranges. (function () { app.beginUndoGroup("Randomize X/Y Position"); try { var activeItem = app.project.activeItem; var selectedLayers; var dlg; var grp; var row; var result; var minX, maxX, minY, maxY; var i; var layer; var pos; var newPos; var processed = 0; var skipped = 0; var currentTime; var keyIndex; if (!(activeItem instanceof CompItem)) { alert("Please open and activate a composition."); return; } selectedLayers = activeItem.selectedLayers; if (selectedLayers.length === 0) { alert("Please select one or more layers."); return; } // --------------------------------------------------------------------- // GUI // --------------------------------------------------------------------- dlg = new Window("dialog", "Randomize X/Y Position"); grp = dlg.add("group"); grp.orientation = "column"; grp.alignChildren = ["fill", "top"]; row = grp.add("group"); row.add("statictext", undefined, "Minimum X:"); var txtMinX = row.add("edittext", undefined, "0"); txtMinX.characters = 10; row = grp.add("group"); row.add("statictext", undefined, "Maximum X:"); var txtMaxX = row.add("edittext", undefined, String(Math.round(activeItem.width))); txtMaxX.characters = 10; row = grp.add("group"); row.add("statictext", undefined, "Minimum Y:"); var txtMinY = row.add("edittext", undefined, "0"); txtMinY.characters = 10; row = grp.add("group"); row.add("statictext", undefined, "Maximum Y:"); var txtMaxY = row.add("edittext", undefined, String(Math.round(activeItem.height))); txtMaxY.characters = 10; var btnGroup = dlg.add("group"); btnGroup.alignment = "center"; btnGroup.add("button", undefined, "OK", { name: "ok" }); btnGroup.add("button", undefined, "Cancel", { name: "cancel" }); result = dlg.show(); if (result !== 1) { return; } minX = parseFloat(txtMinX.text); maxX = parseFloat(txtMaxX.text); minY = parseFloat(txtMinY.text); maxY = parseFloat(txtMaxY.text); if (isNaN(minX) || isNaN(maxX) || isNaN(minY) || isNaN(maxY)) { alert("Please enter valid numeric values."); return; } if (minX > maxX) { alert("Minimum X cannot be greater than Maximum X."); return; } if (minY > maxY) { alert("Minimum Y cannot be greater than Maximum Y."); return; } currentTime = activeItem.time; // --------------------------------------------------------------------- // Process Layers // --------------------------------------------------------------------- for (i = 0; i < selectedLayers.length; i++) { layer = selectedLayers[i]; if (layer === null || layer.locked) { skipped++; continue; } pos = layer.property("Transform").property("Position"); if (pos === null || pos.expressionEnabled) { skipped++; continue; } // Get the current value (interpolated if animated) newPos = pos.valueAtTime(currentTime, false); // Randomize X and Y newPos[0] = minX + Math.random() * (maxX - minX); newPos[1] = minY + Math.random() * (maxY - minY); if (pos.numKeys > 0) { keyIndex = pos.nearestKeyIndex(currentTime); // If a keyframe already exists at the current time, overwrite it. if (Math.abs(pos.keyTime(keyIndex) - currentTime) < 0.0001) { pos.setValueAtKey(keyIndex, newPos); } else { // Otherwise create a new keyframe. pos.setValueAtTime(currentTime, newPos); } } else { // No keyframes yet, so create the first one. pos.setValueAtTime(currentTime, newPos); } processed++; } alert( "Randomize X/Y Position Complete\n\n" + "Layers Selected: " + selectedLayers.length + "\n" + "Layers Updated: " + processed + "\n" + "Layers Skipped: " + skipped + "\n\n" + "X Range: " + minX + " to " + maxX + "\n" + "Y Range: " + minY + " to " + maxY ); } catch (err) { alert("Error:\n\n" + err.toString()); } finally { app.endUndoGroup(); } })();