Advanced Layer Randomizer Rig for After Effects (v2.0)
A Procedural Variation System for Motion Designers
Motion design often benefits from controlled randomness. Slight variations in position, rotation, scale, and opacity can transform rigid layouts into organic, natural-looking animations.
However, manually randomizing dozens or hundreds of layers inside Adobe After Effects is time-consuming and difficult to maintain. Designers typically have to adjust each layer individually or rely on temporary scripts that do not remain editable later.
The Advanced Layer Randomizer Rig v2.0 solves this by creating a fully procedural randomization system. Instead of permanently modifying layers, the script applies expressions driven by a central controller. This allows designers to generate randomness while keeping full control over intensity, spread, and variation.
Once the rig is built, randomness can be adjusted live using sliders without rebuilding the setup.

Why Randomization Matters in Motion Graphics
Perfect symmetry often looks artificial in motion graphics. In many design situations, small variations make animations feel more natural and visually interesting.
Randomization is useful for:
• particle-like motion
• organic layouts
• UI micro-interactions
• background motion systems
• abstract design animation
• generative graphics
Rather than manually creating these variations, the randomizer rig distributes them automatically.
Core Concept of the Randomizer Rig
The script works by creating a Controller Layer named:
Randomizer_Controller
This layer contains sliders and checkboxes that control how randomness is applied to all rigged layers.
Each target layer receives expressions on its transform properties:
• Position
• Rotation
• Scale
• Opacity
These expressions reference the controller layer so that all changes remain live and adjustable.
Procedural Random Positioning
The position expression introduces randomized offsets around the original position.
The maximum spread is controlled independently for each axis:
• Max Pos X
• Max Pos Y
• Max Pos Z
Each layer receives a unique random offset generated using a seeded random function.
Example logic:
seedRandom(index * seedOffset + seedValue, true)
random(-maxSpread, maxSpread)
Because the randomness is seeded, results remain stable between frames and renders.
Changing the seed generates an entirely new layout.
Intensity Control
The Intensity slider acts as a master multiplier.
Instead of modifying every parameter individually, designers can scale the strength of the randomization globally.
Examples:
Intensity = 0
→ no randomization
Intensity = 100
→ full variation
Intensity = 200
→ exaggerated variation
This makes it very easy to animate randomness gradually.
Seed and Seed Offset
Randomization relies on deterministic seeds so that results remain predictable.
The rig includes two seed parameters:
Random Seed
Controls the base randomness pattern.
Changing this value produces a completely different arrangement.
Seed Offset
Controls how different layers receive unique seeds.
This prevents multiple layers from receiving identical values.
Together these two parameters create a robust and controllable randomization system.
Position Spread Controls
Randomization is applied independently on three axes:
• Max Pos X
• Max Pos Y
• Max Pos Z
This allows designers to control spatial distribution precisely.
Examples:
UI layout variation
Max X = 80
Max Y = 20
Creates subtle horizontal shifts.
Particle spread
Max X = 600
Max Y = 600
Creates wide radial distribution.
3D depth variation
Max Z > 0
Adds randomized depth for 3D layers.
Rotation Randomization
The rig also introduces random rotation offsets.
The Max Rotation parameter defines the maximum rotational deviation for each layer.
Example:
Max Rotation = 45°
Each layer can rotate anywhere between:
−45° and +45°
This is particularly useful for:
• scattered design elements
• hand-drawn style motion
• abstract layouts
Procedural Scale Variation
Scale variation adds visual diversity to repeated elements.
The rig includes:
Scale Variance %
Each layer receives a random scale offset within the defined range.
Example:
Scale Variance = 20%
Results in scale values between:
−20% and +20%
relative to the original scale.
Uniform Scale Option
By default, scale randomization can occur independently on X, Y, and Z axes.
However, this may distort certain elements.
The Uniform Scale option ensures that scaling occurs proportionally across all axes.
This maintains aspect ratio while still allowing random variation.
Opacity Variation
Opacity can also be randomized using the Opacity Variance % parameter.
Example:
Opacity Variance = 30%
Layers may vary between:
70% and 130%
opacity relative to the original value.
This is extremely useful for:
• atmospheric background motion
• particle effects
• layered graphic textures
Layer Selection Modes
The script offers three flexible methods for determining which layers should be randomized.
Prefix Mode
Targets layers whose names begin with a specific prefix.
Example:
Layer_01
Layer_02
Layer_03
Using the prefix “Layer_” automatically collects all matching layers.
This is ideal for large compositions with many elements.
Selected Layers Mode
Applies the rig only to layers currently selected in the timeline.
This is the fastest workflow for small setups.
Duplicate Selected Mode
Creates multiple duplicates of selected layers before applying the rig.
Example:
Duplicate Selected × 5
Each source layer becomes five additional randomized layers.
This makes it extremely easy to create dense procedural layouts.
Anchor Point Centering
Incorrect anchor points often cause unexpected behavior when applying position offsets or rotation.
The script includes an option to automatically center the anchor point based on the layer’s bounding box.
This ensures predictable randomization behavior.
Parenting to Controller
Processed layers can optionally be parented to the controller layer.
Benefits include:
• moving the entire system easily
• animating group transformations
• rotating the whole layout at once
The controller effectively becomes the root object for the rig.
Layer Color Organization
For large compositions, identifying rigged layers quickly is useful.
The script includes a Rig Color option that automatically assigns label colors to:
• controller layer
• processed layers
This keeps complex timelines organized.
Live Settings Panel
One of the most powerful features of the rig is the Live Settings panel.
Every parameter in the controller can be modified directly from the script interface.
When sliders are adjusted:
• controller values update immediately
• expressions recalculate in real time
• the composition refreshes automatically
This allows designers to experiment freely without rebuilding the rig.
Removing the Rig
If designers need to revert layers back to normal behavior, the script provides a Remove Rig button.
This simply clears expressions from the affected transform properties while leaving the layers intact.
The process is completely reversible thanks to the After Effects undo system.
Ideal Use Cases
The Advanced Layer Randomizer Rig is extremely versatile.
Designers commonly use it for:
• particle-style motion systems
• background animation
• abstract motion graphics
• scattered UI elements
• procedural design experiments
• generative animation systems
Because the randomization is fully procedural, the system can be animated over time to create evolving visual patterns.
Performance Considerations
The rig was designed with performance in mind.
Key optimizations include:
• safe expression handling
• minimal refresh calls
• deterministic random seeds
• optimized property detection
These improvements allow the script to work reliably even with large numbers of layers.
Procedural Design Advantages
Procedural motion design tools offer several advantages over manual workflows:
• rapid experimentation
• easy iteration
• reusable setups
• non-destructive editing
Instead of rebuilding layouts repeatedly, designers simply adjust a few parameters.
The result is a far more efficient creative process.
Conclusion
The Advanced Layer Randomizer Rig v2.0 provides a powerful and flexible way to introduce controlled randomness into motion graphics projects.
By combining expression-driven variation with a central controller, the rig allows designers to generate organic layouts quickly while maintaining full artistic control.
Key capabilities include:
• position randomization
• rotation variation
• scale variation
• opacity randomness
• live parameter adjustment
• multiple layer selection modes
For motion designers working in Adobe After Effects, this tool can dramatically accelerate the creation of procedural and generative animation systems.
Instead of manually adjusting dozens of layers, designers can focus on exploring creative possibilities.
(function(thisObj) {
// =============================================================================
// ADVANCED LAYER RANDOMIZER RIG v2.0
// =============================================================================
// =========================================================================
// EXPRESSION TEMPLATES
// =========================================================================
var POS_EXPR =[
"try {",
" var ctrl = thisComp.layer('Randomizer_Controller');",
" var intensity = ctrl.effect('Intensity')('Slider') / 100;",
" var maxX = ctrl.effect('Max Pos X')('Slider');",
" var maxY = ctrl.effect('Max Pos Y')('Slider');",
" var maxZ = ctrl.effect('Max Pos Z')('Slider');",
" var seedVal = ctrl.effect('Random Seed')('Slider');",
" var seedOff = ctrl.effect('Seed Offset')('Slider');",
" seedRandom(index * seedOff + seedVal, true);",
" var rx = random(-maxX, maxX);",
" var ry = random(-maxY, maxY);",
" var rz = random(-maxZ, maxZ);",
" var offset = (value.length === 3) ? [rx, ry, rz] : [rx, ry];",
" value + (offset * intensity);",
"} catch(e) { value; }"
].join("\n");
var ROT_EXPR =[
"try {",
" var ctrl = thisComp.layer('Randomizer_Controller');",
" var intensity = ctrl.effect('Intensity')('Slider') / 100;",
" var maxRot = ctrl.effect('Max Rotation')('Slider');",
" var seedVal = ctrl.effect('Random Seed')('Slider');",
" var seedOff = ctrl.effect('Seed Offset')('Slider');",
" seedRandom(index * seedOff + seedVal + 101, true);",
" var off = random(-maxRot, maxRot) * intensity;",
" value + off;",
"} catch(e) { value; }"
].join("\n");
var SCALE_EXPR =[
"try {",
" var ctrl = thisComp.layer('Randomizer_Controller');",
" var intensity = ctrl.effect('Intensity')('Slider') / 100;",
" var sVar = ctrl.effect('Scale Variance %')('Slider');",
" var uniform = ctrl.effect('Uniform Scale')('Checkbox');",
" var seedVal = ctrl.effect('Random Seed')('Slider');",
" var seedOff = ctrl.effect('Seed Offset')('Slider');",
" seedRandom(index * seedOff + seedVal + 202, true);",
" var sx = random(-sVar, sVar);",
" var sy = (uniform == 1) ? sx : random(-sVar, sVar);",
" var sz = (uniform == 1) ? sx : random(-sVar, sVar);",
" var offset = (value.length === 3) ? [sx, sy, sz] : [sx, sy];",
" value + (offset * intensity);",
"} catch(e) { value; }"
].join("\n");
var OPA_EXPR =[
"try {",
" var ctrl = thisComp.layer('Randomizer_Controller');",
" var intensity = ctrl.effect('Intensity')('Slider') / 100;",
" var oVar = ctrl.effect('Opacity Variance %')('Slider');",
" var seedVal = ctrl.effect('Random Seed')('Slider');",
" var seedOff = ctrl.effect('Seed Offset')('Slider');",
" seedRandom(index * seedOff + seedVal + 303, true);",
" var off = random(-oVar, oVar) * intensity;",
" value + off;",
"} catch(e) { value; }"
].join("\n");
// =========================================================================
// HELPERS
// =========================================================================
function safeRefresh() {
try { app.refresh(); } catch(e) {}
}
function getComp() {
var c = app.project.activeItem;
return (c && c instanceof CompItem) ? c : null;
}
function getController(comp) {
try { return comp.layer("Randomizer_Controller"); } catch(e) { return null; }
}
function getTransformProp(layer, kind) {
var tg = layer.property("ADBE Transform Group");
if (!tg) return null;
var map = {
anchorPoint :["ADBE Anchor Point"],
position :["ADBE Position", "ADBE Position_0"],
rotation :["ADBE Rotate Z", "ADBE Rotation"],
scale :["ADBE Scale"],
opacity :["ADBE Opacity"]
};
var tries = map[kind] ||[];
for (var i = 0; i < tries.length; i++) {
try { var p = tg.property(tries[i]); if (p) return p; } catch(e) {}
}
var disp = {anchorPoint:"Anchor Point", position:"Position",
rotation:"Rotation", scale:"Scale", opacity:"Opacity"}[kind];
for (var n = 1; n <= tg.numProperties; n++) {
try { var s = tg.property(n); if (s && s.name === disp) return s; } catch(e) {}
}
return null;
}
function centerAnchorPoint(layer) {
try {
var tg = layer.property("ADBE Transform Group");
var ap = tg.property("ADBE Anchor Point");
var pos = tg.property("ADBE Position");
if (!ap || !pos) return;
var r = layer.sourceRectAtTime(0, false);
var ncx = r.left + r.width / 2;
var ncy = r.top + r.height / 2;
var oldApExpr = ap.expression;
var oldPosExpr = pos.expression;
var curPos = pos.value;
var curAp = ap.value;
if (oldApExpr) ap.expression = "";
if (oldPosExpr) pos.expression = "";
ap.setValue([ncx, ncy]);
pos.setValue([curPos[0] + (ncx - curAp[0]), curPos[1] + (ncy - curAp[1])]);
if (oldApExpr) ap.expression = oldApExpr;
if (oldPosExpr) pos.expression = oldPosExpr;
} catch(e) {}
}
function getEffectProp1(comp, fxName) {
var ctrl = getController(comp);
if (!ctrl) return null;
var fx = ctrl.property("ADBE Effect Parade");
for (var e = 1; e <= fx.numProperties; e++) {
if (fx.property(e).name === fxName) {
try { return fx.property(e).property(1); } catch(ee) {}
}
}
return null;
}
function syncToAE(effectName, val, isDragging) {
var comp = getComp();
if (!comp) return;
var prop = getEffectProp1(comp, effectName);
if (!prop) return;
if (isDragging) {
try { prop.setValue(val); } catch(e) {}
} else {
app.beginUndoGroup("Rig: " + effectName);
try { prop.setValue(val); } catch(e) {}
app.endUndoGroup();
}
}
function ensureEffect(fx, matchName, displayName, defVal) {
for (var e = 1; e <= fx.numProperties; e++) {
if (fx.property(e).name === displayName) return;
}
try {
var p = fx.addProperty(matchName);
p.name = displayName;
p.property(1).setValue(defVal);
} catch(err) {}
}
function removeAllExpressions(layer) {
var ks =["position","rotation","scale","opacity"];
for (var i = 0; i < ks.length; i++) {
try { var p = getTransformProp(layer, ks[i]); if (p) p.expression = ""; } catch(e) {}
}
}
function rigLayer(lyr, cx, cy, doCentre, doParent, ctrlLyr) {
var pPos = getTransformProp(lyr, "position");
var pRot = getTransformProp(lyr, "rotation");
var pScl = getTransformProp(lyr, "scale");
var pOpa = getTransformProp(lyr, "opacity");
if (pPos) {
try {
if (typeof pPos.dimensionsSeparated !== 'undefined' && pPos.dimensionsSeparated) {
pPos.dimensionsSeparated = false;
}
if (doCentre && pPos.expression === "") {
var oldParent = lyr.parent;
lyr.parent = null;
pPos.setValue(pPos.value.length === 3 ? [cx, cy, 0] : [cx, cy]);
lyr.parent = oldParent;
}
} catch(e) {}
}
if (doParent && ctrlLyr) {
lyr.parent = ctrlLyr;
}
if (pPos) { try { pPos.expression = POS_EXPR; } catch(e) {} }
if (pRot) { try { pRot.expression = ROT_EXPR; } catch(e) {} }
if (pScl) { try { pScl.expression = SCALE_EXPR; } catch(e) {} }
if (pOpa) { try { pOpa.expression = OPA_EXPR; } catch(e) {} }
}
// =========================================================================
// UI
// =========================================================================
function buildUI(thisObj) {
var win = (thisObj instanceof Panel)
? thisObj
: new Window("palette", "Layer Randomizer Rig v2.0", undefined, {resizeable:true});
win.orientation = "column";
win.alignChildren =["fill","top"];
win.spacing = 7;
win.margins = 12;
var statusBar = win.add("statictext", undefined, "Ready.");
statusBar.alignment =["fill","top"];
statusBar.justify = "center";
function setStatus(msg) { statusBar.text = msg; }
// =====================================================================
// PANEL 1 — SETUP
// =====================================================================
var pSetup = win.add("panel", undefined, "1. Setup");
pSetup.orientation = "column";
pSetup.alignChildren =["fill","top"];
pSetup.spacing = 5;
pSetup.margins =[10,14,10,10];
var modeGrp = pSetup.add("group");
modeGrp.orientation = "column";
modeGrp.alignChildren =["left","top"];
modeGrp.spacing = 4;
var r1 = modeGrp.add("group");
r1.orientation = "row";
r1.alignChildren =["left","center"];
var rbPrefix = r1.add("radiobutton", undefined, "Layer Prefix:");
var prefixInput = r1.add("edittext", undefined, "Layer_");
prefixInput.preferredSize.width = 90;
var r2 = modeGrp.add("group");
r2.orientation = "row";
r2.alignChildren =["left","center"];
var rbSelected = r2.add("radiobutton", undefined, "Use Selected Layers");
var r3 = modeGrp.add("group");
r3.orientation = "row";
r3.alignChildren = ["left","center"];
var rbDuplicate = r3.add("radiobutton", undefined, "Duplicate Selected \u00D7");
var dupCountInput = r3.add("edittext", undefined, "5");
dupCountInput.preferredSize.width = 34;
var currentMode = "prefix";
function setMode(which) {
currentMode = which;
rbPrefix.value = (which === "prefix");
rbSelected.value = (which === "selected");
rbDuplicate.value = (which === "duplicate");
prefixInput.enabled = (which === "prefix");
dupCountInput.enabled = (which === "duplicate");
}
setMode("prefix");
rbPrefix.onClick = function() { setMode("prefix"); };
rbSelected.onClick = function() { setMode("selected"); };
rbDuplicate.onClick = function() { setMode("duplicate"); };
var optsGrp = pSetup.add("group");
optsGrp.orientation = "column";
optsGrp.alignChildren = ["left", "top"];
optsGrp.spacing = 3;
var cbCenterCtrl = optsGrp.add("checkbox", undefined, "Place Controller at Comp Centre");
var cbCenterLayers = optsGrp.add("checkbox", undefined, "Move Target Layers to Comp Centre");
var cbCenterAnchor = optsGrp.add("checkbox", undefined, "Centre Anchor Point on Each Layer");
var cbParentLayer = optsGrp.add("checkbox", undefined, "Parent Processed Layers to Controller");
cbCenterCtrl.value = true;
cbCenterLayers.value = false;
cbCenterAnchor.value = false;
cbParentLayer.value = true;
var rColor = pSetup.add("group");
rColor.orientation = "row";
rColor.alignChildren = ["left", "center"];
rColor.add("statictext", undefined, "Set Rig Color:");
var dropLabel = rColor.add("dropdownlist", undefined,["(Don't Change)", "1: Red", "2: Yellow", "3: Aqua", "4: Pink", "5: Lavender", "6: Peach", "7: Sea Foam", "8: Blue", "9: Green", "10: Purple", "11: Orange", "12: Brown", "13: Fuchsia", "14: Cyan", "15: Sandstone", "16: Dark Grn"]);
dropLabel.selection = 0;
dropLabel.preferredSize.width = 110;
var actGrp = pSetup.add("group");
actGrp.orientation = "row";
actGrp.margins =[0,5,0,0];
var btnBuild = actGrp.add("button", undefined, "\u25B6 Build / Update Rig");
var btnClear = actGrp.add("button", undefined, "\u2715 Remove Rig");
// =====================================================================
// PANEL 2 — LIVE SETTINGS
// =====================================================================
var pLive = win.add("panel", undefined, "2. Live Settings");
pLive.orientation = "column";
pLive.alignChildren = ["fill","top"];
pLive.spacing = 4;
pLive.margins =[10,14,10,10];
var LABEL_W = 100;
function addRow(parent, label, minV, maxV, defVal, dp) {
dp = (dp === undefined) ? 0 : dp;
var g = parent.add("group");
g.orientation = "row";
g.alignChildren =["left","center"];
g.margins =[0,1,0,1];
var lbl = g.add("statictext", undefined, label + ":");
lbl.preferredSize.width = LABEL_W;
var sl = g.add("slider", undefined, defVal, minV, maxV);
sl.preferredSize =[110, 18];
sl.minimumSize =[60, 16];
var et = g.add("edittext", undefined, defVal.toFixed(dp));
et.preferredSize.width = 44;
et.justify = "right";
sl.onChanging = function() {
var v = parseFloat(sl.value.toFixed(dp));
et.text = v.toFixed(dp);
syncToAE(label, v, true);
};
sl.onChange = function() {
var v = parseFloat(sl.value.toFixed(dp));
et.text = v.toFixed(dp);
syncToAE(label, v, false);
};
et.onChange = function() {
var v = parseFloat(et.text);
if (isNaN(v)) v = defVal;
v = Math.max(minV, Math.min(maxV, v));
sl.value = v;
et.text = v.toFixed(dp);
syncToAE(label, v, false);
};
return {
slider: sl, text: et,
getValue: function() { return parseFloat(sl.value.toFixed(dp)); }
};
}
function secLabel(parent, txt) {
var g = parent.add("group");
g.margins =[0,5,0,0];
var st = g.add("statictext", undefined, txt.toUpperCase());
st.graphics.font = ScriptUI.newFont("dialog", "BOLD", 10);
}
// ── GLOBAL ────────────────────────────────────────────────────────────
secLabel(pLive, "Global");
var uiIntensity = addRow(pLive, "Intensity", 0, 200, 100);
var uiSeed = addRow(pLive, "Random Seed", 0, 999, 1);
var uiSeedOff = addRow(pLive, "Seed Offset", 1, 100, 10);
// ── POSITION ─────────────────────────────────────────────────────────
secLabel(pLive, "Position (Spread)");
var uiMaxX = addRow(pLive, "Max Pos X", 0, 4000, 500);
var uiMaxY = addRow(pLive, "Max Pos Y", 0, 4000, 500);
var uiMaxZ = addRow(pLive, "Max Pos Z", 0, 4000, 0);
// ── ROTATION & SCALE ──────────────────────────────────────────────────
secLabel(pLive, "Rotation & Scale");
var uiRotMax = addRow(pLive, "Max Rotation", 0, 1440, 0);
var uiScale = addRow(pLive, "Scale Variance %", 0, 500, 0);
var uniGrp = pLive.add("group");
uniGrp.orientation = "row";
uniGrp.alignChildren = ["left","center"];
var uniSpc = uniGrp.add("statictext", undefined, "");
uniSpc.preferredSize.width = LABEL_W;
var cbUniform = uniGrp.add("checkbox", undefined, "Uniform Scale");
cbUniform.value = true;
cbUniform.onClick = function() {
syncToAE("Uniform Scale", cbUniform.value ? 1 : 0, false);
};
// ── OPACITY ───────────────────────────────────────────────────────────
secLabel(pLive, "Opacity");
var uiOpacity = addRow(pLive, "Opacity Variance %", 0, 100, 0);
// ── Info + bottom buttons ─────────────────────────────────────────────
var infoTxt = win.add("statictext", undefined, "Rigged layers: \u2014");
infoTxt.alignment =["fill","top"];
infoTxt.justify = "center";
var btnRow = win.add("group");
btnRow.alignment = "right";
var btnClose = btnRow.add("button", undefined, "Close");
// =====================================================================
// BUILD LOGIC
// =====================================================================
function executeBuild(andClose) {
var comp = getComp();
if (!comp) { alert("Please activate a composition first."); return false; }
var mode = currentMode;
var prefix = prefixInput.text;
if (mode === "prefix" && prefix === "") {
alert("Layer Prefix cannot be empty."); return false;
}
var dupCount = parseInt(dupCountInput.text, 10);
if (mode === "duplicate" && (isNaN(dupCount) || dupCount < 1)) {
alert("Duplicate count must be a positive integer."); return false;
}
var snapshotIndices =[];
if (mode === "selected" || mode === "duplicate") {
var rawSel = comp.selectedLayers;
for (var si = 0; si < rawSel.length; si++) {
if (rawSel[si].name !== "Randomizer_Controller") snapshotIndices.push(rawSel[si].index);
}
if (snapshotIndices.length === 0) {
alert(mode === "duplicate"
? "Select source layer(s) in the timeline before building."
: "No layers selected (excluding Controller).");
return false;
}
}
app.beginUndoGroup("Advanced Randomizer Rig: Build");
var errMsg = null;
var targets =[];
var total = 0;
var chosenLabel = dropLabel.selection.index;
try {
var cx = comp.width / 2;
var cy = comp.height / 2;
if (mode === "duplicate") {
for (var s = 0; s < snapshotIndices.length; s++) {
var srcLayer = comp.layer(snapshotIndices[s]);
if (srcLayer.locked) srcLayer.locked = false;
var baseName = srcLayer.name;
var group =[srcLayer];
for (var di = 0; di < dupCount; di++) {
var dup = srcLayer.duplicate();
group.push(dup);
}
group.sort(function(a, b) { return a.index - b.index; });
for (var ni = 0; ni < group.length; ni++) {
group[ni].name = baseName + "_" + (ni + 1);
targets.push(group[ni]);
}
}
mode = "duplicate_done";
}
if (!errMsg && mode !== "duplicate_done") {
if (mode === "selected") {
for (var si = 0; si < snapshotIndices.length; si++) {
targets.push(comp.layer(snapshotIndices[si]));
}
} else if (mode === "prefix") {
for (var li = 1; li <= comp.numLayers; li++) {
var lyr = comp.layer(li);
if (lyr.name !== "Randomizer_Controller" && lyr.name.indexOf(prefix) === 0) {
targets.push(lyr);
}
}
}
}
if (targets.length > 0) {
targets.sort(function(a, b) { return a.index - b.index; });
}
total = targets.length;
if (!errMsg && total === 0) {
errMsg = (mode === "selected")
? "No layers selected (excluding Controller)."
: "No layers found with prefix: \"" + prefix + "\"";
}
if (!errMsg && total > 0) {
// ── Controller ────────────────────────────────────────────
var ctrlLayer = getController(comp);
if (!ctrlLayer) {
ctrlLayer = comp.layers.addNull();
ctrlLayer.name = "Randomizer_Controller";
}
if (chosenLabel > 0) {
ctrlLayer.label = chosenLabel;
} else if (ctrlLayer.label === 0) {
ctrlLayer.label = 9; // Default Green if nothing chosen
}
if (cbCenterCtrl.value && ctrlLayer.property("ADBE Transform Group").property("ADBE Position").expression === "") {
ctrlLayer.property("ADBE Transform Group")
.property("ADBE Position").setValue([cx, cy]);
}
ctrlLayer.shy = false;
ctrlLayer.locked = false;
var fx = ctrlLayer.property("ADBE Effect Parade");
ensureEffect(fx, "ADBE Slider Control", "Intensity", uiIntensity.getValue());
ensureEffect(fx, "ADBE Slider Control", "Random Seed", uiSeed.getValue());
ensureEffect(fx, "ADBE Slider Control", "Seed Offset", uiSeedOff.getValue());
ensureEffect(fx, "ADBE Slider Control", "Max Pos X", uiMaxX.getValue());
ensureEffect(fx, "ADBE Slider Control", "Max Pos Y", uiMaxY.getValue());
ensureEffect(fx, "ADBE Slider Control", "Max Pos Z", uiMaxZ.getValue());
ensureEffect(fx, "ADBE Slider Control", "Max Rotation", uiRotMax.getValue());
ensureEffect(fx, "ADBE Slider Control", "Scale Variance %", uiScale.getValue());
ensureEffect(fx, "ADBE Checkbox Control", "Uniform Scale", cbUniform.value ? 1 : 0);
ensureEffect(fx, "ADBE Slider Control", "Opacity Variance %", uiOpacity.getValue());
// ── Per-layer ─────────────────────────────────────────────
for (var k = 0; k < total; k++) {
var lk = targets[k];
if (chosenLabel > 0) lk.label = chosenLabel;
if (cbCenterAnchor.value) centerAnchorPoint(lk);
rigLayer(lk, cx, cy, cbCenterLayers.value, cbParentLayer.value, ctrlLayer);
}
}
} catch(err) {
errMsg = "Build failed:\n" + err.toString();
}
app.endUndoGroup();
if (errMsg) {
alert(errMsg);
safeRefresh();
return false;
}
var src = (currentMode === "selected") ? "selection" : (currentMode === "duplicate") ? "duplicated copies" : "prefix \"" + prefix + "\"";
infoTxt.text = "Rigged " + total + " layers (" + src + ")";
setStatus("\u2714 Rig built on " + total + " layers.");
safeRefresh();
if (andClose && win instanceof Window) win.close();
return true;
}
function executeClear() {
var comp = getComp();
if (!comp) { alert("Select a composition first."); return; }
var clearTargets =[];
if (currentMode === "prefix") {
var px = prefixInput.text;
if (px === "") { alert("Layer Prefix cannot be empty."); return; }
for (var i = 1; i <= comp.numLayers; i++) {
var lyr = comp.layer(i);
if (lyr.name !== "Randomizer_Controller" && lyr.name.indexOf(px) === 0)
clearTargets.push(lyr);
}
} else {
var rawSel = comp.selectedLayers;
for (var si = 0; si < rawSel.length; si++) {
if (rawSel[si].name !== "Randomizer_Controller") clearTargets.push(rawSel[si]);
}
}
if (clearTargets.length === 0) {
alert((currentMode === "prefix") ? "No target layers found with prefix." : "No valid layers selected to clear.");
return;
}
app.beginUndoGroup("Remove Randomizer Rig Expressions");
for (var j = 0; j < clearTargets.length; j++) removeAllExpressions(clearTargets[j]);
app.endUndoGroup();
infoTxt.text = "Rigged layers: \u2014";
setStatus("Expressions removed from " + clearTargets.length + " layers.");
safeRefresh();
}
btnBuild.onClick = function() { executeBuild(false); };
btnClear.onClick = executeClear;
btnClose.onClick = function() { if (win instanceof Window) win.close(); };
win.layout.layout(true);
win.layout.resize();
return win;
}
// =========================================================================
// LAUNCH
// =========================================================================
var myWin = buildUI(thisObj);
if (myWin instanceof Window) { myWin.center(); myWin.show(); }
})(this);Update (Added Color Options)
(function(thisObj) {
// =============================================================================
// ADVANCED LAYER RANDOMIZER RIG v3.0
// =============================================================================
// =========================================================================
// EXPRESSION TEMPLATES
// =========================================================================
var POS_EXPR =[
"try {",
" var ctrl = thisComp.layer('Randomizer_Controller');",
" var intensity = ctrl.effect('Intensity')('Slider') / 100;",
" var maxX = ctrl.effect('Max Pos X')('Slider');",
" var maxY = ctrl.effect('Max Pos Y')('Slider');",
" var maxZ = ctrl.effect('Max Pos Z')('Slider');",
" var seedVal = ctrl.effect('Random Seed')('Slider');",
" var seedOff = ctrl.effect('Seed Offset')('Slider');",
" seedRandom(index * seedOff + seedVal, true);",
" var rx = random(-maxX, maxX);",
" var ry = random(-maxY, maxY);",
" var rz = random(-maxZ, maxZ);",
" var offset = (value.length === 3) ? [rx, ry, rz] :[rx, ry];",
" value + (offset * intensity);",
"} catch(e) { value; }"
].join("\n");
var ROT_EXPR =[
"try {",
" var ctrl = thisComp.layer('Randomizer_Controller');",
" var intensity = ctrl.effect('Intensity')('Slider') / 100;",
" var maxRot = ctrl.effect('Max Rotation')('Slider');",
" var seedVal = ctrl.effect('Random Seed')('Slider');",
" var seedOff = ctrl.effect('Seed Offset')('Slider');",
" seedRandom(index * seedOff + seedVal + 101, true);",
" var off = random(-maxRot, maxRot) * intensity;",
" value + off;",
"} catch(e) { value; }"
].join("\n");
var SCALE_EXPR =[
"try {",
" var ctrl = thisComp.layer('Randomizer_Controller');",
" var intensity = ctrl.effect('Intensity')('Slider') / 100;",
" var sMin = ctrl.effect('Min Scale')('Slider');",
" var sMax = ctrl.effect('Max Scale')('Slider');",
" var gScale = ctrl.effect('Global Scale')('Slider') / 100;",
" var uniform = ctrl.effect('Uniform Scale')('Checkbox');",
" var seedVal = ctrl.effect('Random Seed')('Slider');",
" var seedOff = ctrl.effect('Seed Offset')('Slider');",
" seedRandom(index * seedOff + seedVal + 202, true);",
" var rx = random(sMin, sMax);",
" var ry = (uniform == 1) ? rx : random(sMin, sMax);",
" var rz = (uniform == 1) ? rx : random(sMin, sMax);",
" var targetX = linear(intensity, 0, 1, 100, rx);",
" var targetY = linear(intensity, 0, 1, 100, ry);",
" var targetZ = linear(intensity, 0, 1, 100, rz);",
" var s = (value.length === 3) ? [targetX, targetY, targetZ] :[targetX, targetY];",
" var out =[];",
" for(var i=0; i<value.length; i++) out[i] = value[i] * (s[i] / 100) * gScale;",
" out;",
"} catch(e) { value; }"
].join("\n");
var OPA_EXPR =[
"try {",
" var ctrl = thisComp.layer('Randomizer_Controller');",
" var intensity = ctrl.effect('Intensity')('Slider') / 100;",
" var oMin = ctrl.effect('Min Opacity')('Slider');",
" var oMax = ctrl.effect('Max Opacity')('Slider');",
" var seedVal = ctrl.effect('Random Seed')('Slider');",
" var seedOff = ctrl.effect('Seed Offset')('Slider');",
" seedRandom(index * seedOff + seedVal + 303, true);",
" var r = random(oMin, oMax);",
" var targetOpa = linear(intensity, 0, 1, 100, r);",
" value * (targetOpa / 100);",
"} catch(e) { value; }"
].join("\n");
var COLOR_EXPR =[
"try {",
" var ctrl = thisComp.layer('Randomizer_Controller');",
" var count = Math.max(2, Math.min(4, Math.round(ctrl.effect('Active Colors')('Slider'))));",
" var trans = ctrl.effect('Color Transition %')('Slider') / 100;",
" var seedVal = ctrl.effect('Random Seed')('Slider');",
" var seedOff = ctrl.effect('Seed Offset')('Slider');",
" var colors =[",
" ctrl.effect('Color 1')('Color'),",
" ctrl.effect('Color 2')('Color'),",
" ctrl.effect('Color 3')('Color'),",
" ctrl.effect('Color 4')('Color')",
" ];",
" seedRandom(index * seedOff + seedVal + 404, true);",
" var r = random(0, count - 1);",
" var idx = Math.floor(r);",
" if (idx >= count - 1) idx = count - 2;",
" var t = r - idx;",
" var blendedColor = linear(t, 0, 1, colors[idx], colors[idx+1]);",
" var exactColor = colors[Math.round(r)];",
" linear(trans, 0, 1, exactColor, blendedColor);",
"} catch(e) { value; }"
].join("\n");
// =========================================================================
// HELPERS
// =========================================================================
function safeRefresh() {
try { app.refresh(); } catch(e) {}
}
function getComp() {
var c = app.project.activeItem;
return (c && c instanceof CompItem) ? c : null;
}
function getController(comp) {
try { return comp.layer("Randomizer_Controller"); } catch(e) { return null; }
}
function getTransformProp(layer, kind) {
var tg = layer.property("ADBE Transform Group");
if (!tg) return null;
var map = {
anchorPoint :["ADBE Anchor Point"],
position :["ADBE Position", "ADBE Position_0"],
rotation :["ADBE Rotate Z", "ADBE Rotation"],
scale :["ADBE Scale"],
opacity :["ADBE Opacity"]
};
var tries = map[kind] ||[];
for (var i = 0; i < tries.length; i++) {
try { var p = tg.property(tries[i]); if (p) return p; } catch(e) {}
}
var disp = {anchorPoint:"Anchor Point", position:"Position",
rotation:"Rotation", scale:"Scale", opacity:"Opacity"}[kind];
for (var n = 1; n <= tg.numProperties; n++) {
try { var s = tg.property(n); if (s && s.name === disp) return s; } catch(e) {}
}
return null;
}
function centerAnchorPoint(layer) {
try {
var tg = layer.property("ADBE Transform Group");
var ap = tg.property("ADBE Anchor Point");
var pos = tg.property("ADBE Position");
if (!ap || !pos) return;
var r = layer.sourceRectAtTime(0, false);
var ncx = r.left + r.width / 2;
var ncy = r.top + r.height / 2;
var oldApExpr = ap.expression;
var oldPosExpr = pos.expression;
var curPos = pos.value;
var curAp = ap.value;
if (oldApExpr) ap.expression = "";
if (oldPosExpr) pos.expression = "";
ap.setValue([ncx, ncy]);
pos.setValue([curPos[0] + (ncx - curAp[0]), curPos[1] + (ncy - curAp[1])]);
if (oldApExpr) ap.expression = oldApExpr;
if (oldPosExpr) pos.expression = oldPosExpr;
} catch(e) {}
}
function getEffectProp1(comp, fxName) {
var ctrl = getController(comp);
if (!ctrl) return null;
var fx = ctrl.property("ADBE Effect Parade");
for (var e = 1; e <= fx.numProperties; e++) {
if (fx.property(e).name === fxName) {
try { return fx.property(e).property(1); } catch(ee) {}
}
}
return null;
}
function syncToAE(effectName, val, isDragging) {
var comp = getComp();
if (!comp) return;
var prop = getEffectProp1(comp, effectName);
if (!prop) return;
if (isDragging) {
try { prop.setValue(val); } catch(e) {}
} else {
app.beginUndoGroup("Rig: " + effectName);
try { prop.setValue(val); } catch(e) {}
app.endUndoGroup();
}
}
function ensureEffect(fx, matchName, displayName, defVal) {
for (var e = 1; e <= fx.numProperties; e++) {
if (fx.property(e).name === displayName) return;
}
try {
var p = fx.addProperty(matchName);
p.name = displayName;
p.property(1).setValue(defVal);
} catch(err) {}
}
function removeAllExpressions(layer) {
var ks =["position","rotation","scale","opacity"];
for (var i = 0; i < ks.length; i++) {
try { var p = getTransformProp(layer, ks[i]); if (p) p.expression = ""; } catch(e) {}
}
try { layer.property("ADBE Effect Parade").property("Randomizer Fill").remove(); } catch(e) {}
}
function rigLayer(lyr, cx, cy, doCentre, doParent, ctrlLyr, doColor) {
var pPos = getTransformProp(lyr, "position");
var pRot = getTransformProp(lyr, "rotation");
var pScl = getTransformProp(lyr, "scale");
var pOpa = getTransformProp(lyr, "opacity");
if (pPos) {
try {
if (typeof pPos.dimensionsSeparated !== 'undefined' && pPos.dimensionsSeparated) {
pPos.dimensionsSeparated = false;
}
if (doCentre && pPos.expression === "") {
var oldParent = lyr.parent;
lyr.parent = null;
pPos.setValue(pPos.value.length === 3 ? [cx, cy, 0] : [cx, cy]);
lyr.parent = oldParent;
}
} catch(e) {}
}
if (doParent && ctrlLyr) {
lyr.parent = ctrlLyr;
}
if (pPos) { try { pPos.expression = POS_EXPR; } catch(e) {} }
if (pRot) { try { pRot.expression = ROT_EXPR; } catch(e) {} }
if (pScl) { try { pScl.expression = SCALE_EXPR; } catch(e) {} }
if (pOpa) { try { pOpa.expression = OPA_EXPR; } catch(e) {} }
// Color Injection
if (doColor) {
var fxParade = lyr.property("ADBE Effect Parade");
var fillFx = fxParade.property("Randomizer Fill");
if (!fillFx) {
fillFx = fxParade.addProperty("ADBE Fill");
fillFx.name = "Randomizer Fill";
}
// Using explicit indices (3=Color, 7=Opacity) to ensure cross-language compatibility
try { fillFx.property(3).expression = COLOR_EXPR; } catch(e) {}
try { fillFx.property(7).expression = "try { thisComp.layer('Randomizer_Controller').effect('Intensity')('Slider') } catch(e) { value; }"; } catch(e) {}
}
}
// =========================================================================
// UI
// =========================================================================
function buildUI(thisObj) {
var win = (thisObj instanceof Panel)
? thisObj
: new Window("palette", "Layer Randomizer Rig v3.0", undefined, {resizeable:true});
win.orientation = "column";
win.alignChildren =["fill","top"];
win.spacing = 7;
win.margins = 12;
var statusBar = win.add("statictext", undefined, "Ready.");
statusBar.alignment =["fill","top"];
statusBar.justify = "center";
function setStatus(msg) { statusBar.text = msg; }
// =====================================================================
// PANEL 1 — SETUP
// =====================================================================
var pSetup = win.add("panel", undefined, "1. Setup");
pSetup.orientation = "column";
pSetup.alignChildren =["fill","top"];
pSetup.spacing = 5;
pSetup.margins =[10,14,10,10];
var modeGrp = pSetup.add("group");
modeGrp.orientation = "column";
modeGrp.alignChildren =["left","top"];
modeGrp.spacing = 4;
var r1 = modeGrp.add("group");
r1.orientation = "row";
r1.alignChildren =["left","center"];
var rbPrefix = r1.add("radiobutton", undefined, "Layer Prefix:");
var prefixInput = r1.add("edittext", undefined, "Layer_");
prefixInput.preferredSize.width = 90;
var r2 = modeGrp.add("group");
r2.orientation = "row";
r2.alignChildren =["left","center"];
var rbSelected = r2.add("radiobutton", undefined, "Use Selected Layers");
var r3 = modeGrp.add("group");
r3.orientation = "row";
r3.alignChildren = ["left","center"];
var rbDuplicate = r3.add("radiobutton", undefined, "Duplicate Selected \u00D7");
var dupCountInput = r3.add("edittext", undefined, "5");
dupCountInput.preferredSize.width = 34;
var currentMode = "prefix";
function setMode(which) {
currentMode = which;
rbPrefix.value = (which === "prefix");
rbSelected.value = (which === "selected");
rbDuplicate.value = (which === "duplicate");
prefixInput.enabled = (which === "prefix");
dupCountInput.enabled = (which === "duplicate");
}
setMode("prefix");
rbPrefix.onClick = function() { setMode("prefix"); };
rbSelected.onClick = function() { setMode("selected"); };
rbDuplicate.onClick = function() { setMode("duplicate"); };
var optsGrp = pSetup.add("group");
optsGrp.orientation = "column";
optsGrp.alignChildren =["left", "top"];
optsGrp.spacing = 3;
var cbCenterCtrl = optsGrp.add("checkbox", undefined, "Place Controller at Comp Centre");
var cbCenterLayers = optsGrp.add("checkbox", undefined, "Move Target Layers to Comp Centre");
var cbCenterAnchor = optsGrp.add("checkbox", undefined, "Centre Anchor Point on Each Layer");
var cbParentLayer = optsGrp.add("checkbox", undefined, "Parent Processed Layers to Controller");
cbCenterCtrl.value = true;
cbCenterLayers.value = false;
cbCenterAnchor.value = false;
cbParentLayer.value = true;
var rColor = pSetup.add("group");
rColor.orientation = "row";
rColor.alignChildren =["left", "center"];
rColor.add("statictext", undefined, "Set Rig Color:");
var dropLabel = rColor.add("dropdownlist", undefined,["(Don't Change)", "1: Red", "2: Yellow", "3: Aqua", "4: Pink", "5: Lavender", "6: Peach", "7: Sea Foam", "8: Blue", "9: Green", "10: Purple", "11: Orange", "12: Brown", "13: Fuchsia", "14: Cyan", "15: Sandstone", "16: Dark Grn"]);
dropLabel.selection = 0;
dropLabel.preferredSize.width = 110;
var actGrp = pSetup.add("group");
actGrp.orientation = "row";
actGrp.margins =[0,5,0,0];
var btnBuild = actGrp.add("button", undefined, "\u25B6 Build / Update Rig");
var btnClear = actGrp.add("button", undefined, "\u2715 Remove Rig");
// =====================================================================
// PANEL 2 — LIVE SETTINGS
// =====================================================================
var pLive = win.add("panel", undefined, "2. Live Settings");
pLive.orientation = "column";
pLive.alignChildren = ["fill","top"];
pLive.spacing = 4;
pLive.margins =[10,14,10,10];
var LABEL_W = 110;
function addRow(parent, label, minV, maxV, defVal, dp) {
dp = (dp === undefined) ? 0 : dp;
var g = parent.add("group");
g.orientation = "row";
g.alignChildren =["left","center"];
g.margins =[0,1,0,1];
var lbl = g.add("statictext", undefined, label + ":");
lbl.preferredSize.width = LABEL_W;
var sl = g.add("slider", undefined, defVal, minV, maxV);
sl.preferredSize =[110, 18];
sl.minimumSize =[60, 16];
var et = g.add("edittext", undefined, defVal.toFixed(dp));
et.preferredSize.width = 44;
et.justify = "right";
sl.onChanging = function() {
var v = parseFloat(sl.value.toFixed(dp));
et.text = v.toFixed(dp);
syncToAE(label, v, true);
};
sl.onChange = function() {
var v = parseFloat(sl.value.toFixed(dp));
et.text = v.toFixed(dp);
syncToAE(label, v, false);
};
et.onChange = function() {
var v = parseFloat(et.text);
if (isNaN(v)) v = defVal;
v = Math.max(minV, Math.min(maxV, v));
sl.value = v;
et.text = v.toFixed(dp);
syncToAE(label, v, false);
};
return {
slider: sl, text: et,
getValue: function() { return parseFloat(sl.value.toFixed(dp)); }
};
}
function secLabel(parent, txt) {
var g = parent.add("group");
g.margins =[0,5,0,0];
var st = g.add("statictext", undefined, txt.toUpperCase());
st.graphics.font = ScriptUI.newFont("dialog", "BOLD", 10);
}
function addDropRow(parent, label, items) {
var g = parent.add("group");
g.orientation = "row";
g.alignChildren =["left","center"];
g.margins = [0,1,0,1];
var lbl = g.add("statictext", undefined, label + ":");
lbl.preferredSize.width = LABEL_W;
var dd = g.add("dropdownlist", undefined, items);
dd.selection = 0;
dd.preferredSize.width = 160;
return dd;
}
// ── GLOBAL ────────────────────────────────────────────────────────────
secLabel(pLive, "Global");
var uiIntensity = addRow(pLive, "Intensity", 0, 200, 100);
var uiSeed = addRow(pLive, "Random Seed", 0, 999, 1);
var uiSeedOff = addRow(pLive, "Seed Offset", 1, 100, 10);
// ── POSITION ─────────────────────────────────────────────────────────
secLabel(pLive, "Position (Spread)");
var uiMaxX = addRow(pLive, "Max Pos X", 0, 4000, 500);
var uiMaxY = addRow(pLive, "Max Pos Y", 0, 4000, 500);
var uiMaxZ = addRow(pLive, "Max Pos Z", 0, 4000, 0);
// ── SCALE ─────────────────────────────────────────────────────────────
secLabel(pLive, "Scale");
var uiScaleMin = addRow(pLive, "Min Scale", 0, 500, 50);
var uiScaleMax = addRow(pLive, "Max Scale", 0, 500, 150);
var uiScaleGbl = addRow(pLive, "Global Scale", 0, 500, 100);
var uniGrp = pLive.add("group");
uniGrp.orientation = "row";
uniGrp.alignChildren = ["left","center"];
var uniSpc = uniGrp.add("statictext", undefined, "");
uniSpc.preferredSize.width = LABEL_W;
var cbUniform = uniGrp.add("checkbox", undefined, "Uniform Scale");
cbUniform.value = true;
cbUniform.onClick = function() {
syncToAE("Uniform Scale", cbUniform.value ? 1 : 0, false);
};
// ── ROTATION & OPACITY ────────────────────────────────────────────────
secLabel(pLive, "Rotation & Opacity");
var uiRotMax = addRow(pLive, "Max Rotation", 0, 1440, 0);
var uiOpaMin = addRow(pLive, "Min Opacity", 0, 100, 0);
var uiOpaMax = addRow(pLive, "Max Opacity", 0, 100, 100);
// ── COLOR ─────────────────────────────────────────────────────────────
secLabel(pLive, "Color Variance");
var cbColorGrp = pLive.add("group");
cbColorGrp.orientation = "row";
var colSpc = cbColorGrp.add("statictext", undefined, "");
colSpc.preferredSize.width = LABEL_W;
var cbColor = cbColorGrp.add("checkbox", undefined, "Enable Color Overlays");
cbColor.value = false;
var uiColorsActive = addDropRow(pLive, "Active Colors", ["2 Colors", "3 Colors", "4 Colors"]);
uiColorsActive.selection = 2; // Default to 4
uiColorsActive.onChange = function() {
syncToAE("Active Colors", uiColorsActive.selection.index + 2, false);
};
var uiColorTrans = addRow(pLive, "Color Transition %", 0, 100, 0);
// ── Info + bottom buttons ─────────────────────────────────────────────
var infoTxt = win.add("statictext", undefined, "Rigged layers: \u2014");
infoTxt.alignment =["fill","top"];
infoTxt.justify = "center";
var btnRow = win.add("group");
btnRow.alignment = "right";
var btnClose = btnRow.add("button", undefined, "Close");
// =====================================================================
// BUILD LOGIC
// =====================================================================
function executeBuild(andClose) {
var comp = getComp();
if (!comp) { alert("Please activate a composition first."); return false; }
var mode = currentMode;
var prefix = prefixInput.text;
if (mode === "prefix" && prefix === "") {
alert("Layer Prefix cannot be empty."); return false;
}
var dupCount = parseInt(dupCountInput.text, 10);
if (mode === "duplicate" && (isNaN(dupCount) || dupCount < 1)) {
alert("Duplicate count must be a positive integer."); return false;
}
var snapshotIndices =[];
if (mode === "selected" || mode === "duplicate") {
var rawSel = comp.selectedLayers;
for (var si = 0; si < rawSel.length; si++) {
if (rawSel[si].name !== "Randomizer_Controller") snapshotIndices.push(rawSel[si].index);
}
if (snapshotIndices.length === 0) {
alert(mode === "duplicate"
? "Select source layer(s) in the timeline before building."
: "No layers selected (excluding Controller).");
return false;
}
}
app.beginUndoGroup("Advanced Randomizer Rig: Build");
var errMsg = null;
var targets =[];
var total = 0;
var chosenLabel = dropLabel.selection.index;
try {
var cx = comp.width / 2;
var cy = comp.height / 2;
if (mode === "duplicate") {
for (var s = 0; s < snapshotIndices.length; s++) {
var srcLayer = comp.layer(snapshotIndices[s]);
if (srcLayer.locked) srcLayer.locked = false;
var baseName = srcLayer.name;
var group =[srcLayer];
for (var di = 0; di < dupCount; di++) {
var dup = srcLayer.duplicate();
group.push(dup);
}
group.sort(function(a, b) { return a.index - b.index; });
for (var ni = 0; ni < group.length; ni++) {
group[ni].name = baseName + "_" + (ni + 1);
targets.push(group[ni]);
}
}
mode = "duplicate_done";
}
if (!errMsg && mode !== "duplicate_done") {
if (mode === "selected") {
for (var si = 0; si < snapshotIndices.length; si++) {
targets.push(comp.layer(snapshotIndices[si]));
}
} else if (mode === "prefix") {
for (var li = 1; li <= comp.numLayers; li++) {
var lyr = comp.layer(li);
if (lyr.name !== "Randomizer_Controller" && lyr.name.indexOf(prefix) === 0) {
targets.push(lyr);
}
}
}
}
if (targets.length > 0) {
targets.sort(function(a, b) { return a.index - b.index; });
}
total = targets.length;
if (!errMsg && total === 0) {
errMsg = (mode === "selected")
? "No layers selected (excluding Controller)."
: "No layers found with prefix: \"" + prefix + "\"";
}
if (!errMsg && total > 0) {
// ── Controller ────────────────────────────────────────────
var ctrlLayer = getController(comp);
if (!ctrlLayer) {
ctrlLayer = comp.layers.addNull();
ctrlLayer.name = "Randomizer_Controller";
}
if (chosenLabel > 0) {
ctrlLayer.label = chosenLabel;
} else if (ctrlLayer.label === 0) {
ctrlLayer.label = 9; // Default Green if nothing chosen
}
if (cbCenterCtrl.value && ctrlLayer.property("ADBE Transform Group").property("ADBE Position").expression === "") {
ctrlLayer.property("ADBE Transform Group")
.property("ADBE Position").setValue([cx, cy]);
}
ctrlLayer.shy = false;
ctrlLayer.locked = false;
var fx = ctrlLayer.property("ADBE Effect Parade");
ensureEffect(fx, "ADBE Slider Control", "Intensity", uiIntensity.getValue());
ensureEffect(fx, "ADBE Slider Control", "Random Seed", uiSeed.getValue());
ensureEffect(fx, "ADBE Slider Control", "Seed Offset", uiSeedOff.getValue());
ensureEffect(fx, "ADBE Slider Control", "Max Pos X", uiMaxX.getValue());
ensureEffect(fx, "ADBE Slider Control", "Max Pos Y", uiMaxY.getValue());
ensureEffect(fx, "ADBE Slider Control", "Max Pos Z", uiMaxZ.getValue());
ensureEffect(fx, "ADBE Slider Control", "Max Rotation", uiRotMax.getValue());
ensureEffect(fx, "ADBE Slider Control", "Min Scale", uiScaleMin.getValue());
ensureEffect(fx, "ADBE Slider Control", "Max Scale", uiScaleMax.getValue());
ensureEffect(fx, "ADBE Slider Control", "Global Scale", uiScaleGbl.getValue());
ensureEffect(fx, "ADBE Checkbox Control", "Uniform Scale", cbUniform.value ? 1 : 0);
ensureEffect(fx, "ADBE Slider Control", "Min Opacity", uiOpaMin.getValue());
ensureEffect(fx, "ADBE Slider Control", "Max Opacity", uiOpaMax.getValue());
ensureEffect(fx, "ADBE Slider Control", "Active Colors", uiColorsActive.selection.index + 2);
ensureEffect(fx, "ADBE Slider Control", "Color Transition %", uiColorTrans.getValue());
// Default Color Palette injection
ensureEffect(fx, "ADBE Color Control", "Color 1", [0.95, 0.20, 0.25, 1]); // Red
ensureEffect(fx, "ADBE Color Control", "Color 2",[0.95, 0.75, 0.15, 1]); // Yellow
ensureEffect(fx, "ADBE Color Control", "Color 3",[0.15, 0.75, 0.85, 1]); // Cyan
ensureEffect(fx, "ADBE Color Control", "Color 4", [0.55, 0.25, 0.85, 1]); // Purple
// ── Per-layer ─────────────────────────────────────────────
for (var k = 0; k < total; k++) {
var lk = targets[k];
if (chosenLabel > 0) lk.label = chosenLabel;
if (cbCenterAnchor.value) centerAnchorPoint(lk);
rigLayer(lk, cx, cy, cbCenterLayers.value, cbParentLayer.value, ctrlLayer, cbColor.value);
}
}
} catch(err) {
errMsg = "Build failed:\n" + err.toString();
}
app.endUndoGroup();
if (errMsg) {
alert(errMsg);
safeRefresh();
return false;
}
var src = (currentMode === "selected") ? "selection" : (currentMode === "duplicate") ? "duplicated copies" : "prefix \"" + prefix + "\"";
infoTxt.text = "Rigged " + total + " layers (" + src + ")";
setStatus("\u2714 Rig built on " + total + " layers.");
safeRefresh();
if (andClose && win instanceof Window) win.close();
return true;
}
function executeClear() {
var comp = getComp();
if (!comp) { alert("Select a composition first."); return; }
var clearTargets =[];
if (currentMode === "prefix") {
var px = prefixInput.text;
if (px === "") { alert("Layer Prefix cannot be empty."); return; }
for (var i = 1; i <= comp.numLayers; i++) {
var lyr = comp.layer(i);
if (lyr.name !== "Randomizer_Controller" && lyr.name.indexOf(px) === 0)
clearTargets.push(lyr);
}
} else {
var rawSel = comp.selectedLayers;
for (var si = 0; si < rawSel.length; si++) {
if (rawSel[si].name !== "Randomizer_Controller") clearTargets.push(rawSel[si]);
}
}
if (clearTargets.length === 0) {
alert((currentMode === "prefix") ? "No target layers found with prefix." : "No valid layers selected to clear.");
return;
}
app.beginUndoGroup("Remove Randomizer Rig Expressions");
for (var j = 0; j < clearTargets.length; j++) removeAllExpressions(clearTargets[j]);
app.endUndoGroup();
infoTxt.text = "Rigged layers: \u2014";
setStatus("Expressions removed from " + clearTargets.length + " layers.");
safeRefresh();
}
btnBuild.onClick = function() { executeBuild(false); };
btnClear.onClick = executeClear;
btnClose.onClick = function() { if (win instanceof Window) win.close(); };
win.layout.layout(true);
win.layout.resize();
return win;
}
// =========================================================================
// LAUNCH
// =========================================================================
var myWin = buildUI(thisObj);
if (myWin instanceof Window) { myWin.center(); myWin.show(); }
})(this);