Expressions & Snippets

Javascript basics

// Declaration
var myVariable;
var 🍎,🍐,🍐
​
// Initialization
var fruit = 🍎;
var fruitArray = [🍏,🍎,🍐,🍊,🍋,🍌,🍉,🍇];
String
"string is text"
​
// For-Loops
for( var i = 0; i < value ; i++){
statement
}
​
​
​
//if/else statements
if (fruit){
statement} else {
statement}
​
// Shorthand if statement
if(x==1) 10;
// Using ternary operation
condition ? exprIfTrue : exprIfFalse
​
Object
var x = {firstName:"John", lastName:"Doe"}; //
​
// Regular Expressions (Regex)
"\n"
​
// Single line comment
​
/* Multi-line comment
Etiam aliquet et mi quis tempor.
Nulla ullamcorper, sapien in molestie rhoncus,
justo magna molestie dolor,
Nullam dignissim interdum mi sit a */
​

Preferred shorthand for naming variables

velocity = v or vel
multiplier = mul;
position = pos, myPos
distance = dst
sourceRect = s
myLayer
myNumLayers

Good practice

  • Declare variable before loop statements

Transformation

Constant value change using Time (drifting)

time*50;
​
// for adding to an alement in an array such as position:
value + [time*50,0]
​
// for value change to start at inPoint
(time-inPoint)*50
​
// Throw (move at a constant speed without keyframes)
veloc = -10; //horizontal velocity (pixels per second)
x = position[0] + (time - inPoint) *veloc;
y = position[1];
[x,y]

Align to X-axis or Y-axis

basic
if X or Y
basic
var dst = 50;
var x= thisComp.layer(index-1).transform.position[0]+dst ;
value + [x,0] ;
if X or Y
var xy = 0;
var dst = 50;
var x= thisComp.layer(index-1).transform.position[0]+dst ;
var x= thisComp.layer(index-1).transform.position[0]+dst
xy==0? value+[x,0]: value+[0,y]; // Ternary conditional operator

Changing anchor point

//Top left
s=sourceRectAtTime();
[0,0];
// or [s.left,s.top] or [s.left,0]
// Return original position
s=sourceRectAtTime(); value-[s.width/2,s.height/2];
​
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
s=sourceRectAtTime();
[0,s.top+s.height/2]+value
​
// Top right
s=sourceRectAtTime();
[s.width,s.top];
​
// ← ← ← ← ←
s=sourceRectAtTime();
[s.left,0]
// or [[s.left,s.height/2]
​
// Center
s=sourceRectAtTime();
[s.left+s.width/2,s.top+s.height/2]+value
​
// → → → → →
s=sourceRectAtTime();
[s.width,s.height/2]
​
// Bot left
s=sourceRectAtTime();
[s.left,s.height];
​
// ↓ ↓ ↓ ↓ ↓
s=sourceRectAtTime();
[s.width/2,s.height];
​
//Bot right
s=sourceRectAtTime();
[s.width,s.height];

Random positioning XYZ

seed = 20;
min = [25,25,-1900]; // Connect to 3D Point Control for control
max = [thisComp.width,thisComp.height,50]
​
seedRandom(seed,true);
​
random(min,max);
​
// This is same as this
​
// x= random(min[0],max[0]);
// y= random(min[1],max[1]);
// z= random(min[2],max[2]);
​
//[x,y,z]

Scale

Maintain Scale When Parented

both axis
uniform
both axis
s = [];
ps = parent.transform.scale.value;
for (i = 0; i < ps.length; i++){
s[i] = value[i]*100/ps[i];
}
s
uniform
arr = value;
ps = parent.transform.scale.value;
ratio = 100/ ps[0];
​
arr*ratio;

Rotation

L = thisComp.layer("Null 1");
v = L.toWorldVec([1,0]);
radiansToDegrees(Math.atan2(v[1],v[0]))

Parent

Avoid inhering rotation

value – parent.transform.rotation
​
​

Opacity

Flickering / Strobe

// VARIABLES
minSeg = 1.5; //minimum interval (must be > 0)
maxSeg = 2.5; //maximum interval (must be > minSeg)
// flickering duration
minFlicker = .5; //must be less than minSeg
maxFlicker = 1; // must be less than minSeg
flickerDur = random(minFlicker,maxFlicker);
//initial conditions
segEndTime = 0;
i = 1;
​
// Continuous loop: create a fixed random segment value and add to segEndTime
while (time >= segEndTime){
i += 1;
seedRandom(i,true);
segEndTime = segEndTime + random(minSeg,maxSeg);
}
​
// Switch back to use the current time as input to the random seed.
seedRandom(1,false);
​
// As time > threshold, flicker
if (time > segEndTime - flickerDur){random(0,100) }else{ 100 }
​
// Source: http://www.motionscript.com/expressions-lab-ae65/swinging-light.html
// Also see: http://www.motionscript.com/mastering-expressions/random-3.html

Random opacity wiggle

// control = thisComp.layer("control"); // connect to null layer with sliders
freq = 1;
amp = 100;
octave = 1;
amp_mult = 3;
​
wiggle(freq, amp, octave, amp_mult, time)

Random opacity wiggle with flicker

// control = thisComp.layer("control"); // connect to null layer with sliders
freq = 1;
amp = 100;
octave = 1;
amp_mult = 3;
​
opacity = wiggle(freq, amp, octave, amp_mult, time)
​
​
// VARIABLES
minSeg = control.effect("minSeg")("Slider"); //minimum interval (must be > 0)
maxSeg = control.effect("maxSeg")("Slider");; //maximum interval (must be > minSeg)
​
// flickering duration
minFlicker = control.effect("minFlicker")("Slider");; //must be less than minSeg
maxFlicker = control.effect("maxFlicker")("Slider");; // must be less than minSeg
flickerDur = random(minFlicker, maxFlicker);
​
//initial conditions
segStartTime = 0;
segEndTime = 0;
i = 1;
​
// Continuous loop: create a fixed random segment value and add to segEndTime
while (time >= segEndTime) {
i += 1;
seedRandom(i, true);
segStartTime = segEndTime;
segEndTime = segEndTime + random(minSeg, maxSeg);
}
​
// Switch back to use the current time as input to the random seed.
seedRandom(1, false);
​
// As time moves threshold, flicker
if (time > segEndTime - flickerDur) {
random(0, 100)
} else {
opacity
}

Random opacity with sin function

// Mass flickering
vel = 50;
seedRandom(0,true);
Math.sin(time*vel+random(index))*100;
// 'Wave flickering'
vel = 50;
seedRandom(0,true);
Math.sin(time*vel+index)*100;
​
// Combining
bool = 1 ;
vel = 50;
seedRandom(0,true);
wave=Math.sin(time*vel+index)*100;
rand =Math.sin(time*vel+random(index))*100;
bool==0 ? wave : rand;

Random opacity turn on

seed = 29;
threshold = linear(time,0,thisComp.duration,0,100); // or Connect to slider to animate switch on & off
​
seedRandom(seed,true);
randValue = random(0,90);
​
if(randValue < threshold){
100;} else 0

Random opacity fade on

v1
v2
v1
seed = 29;
seedRandom(seed,true);
delay = random(0,1);
t = framesToTime(20) // or usethisComp.duration
​
linear(time, 0+delay,t+delay,0,100)
v2
//VARIABLES
fadeDuration = 1;
maxDelay = .5
​
//SETUP
seedRandom(index,true);
delay = random(maxDelay);
t = time -(inPoint + delay);
​
// EXECUTION
linear(t,0,fadeDuration,0,100);

Distance-based opacity fade by Animoplex

// Distance Based Opacity Fade
// Original: https://helpx.adobe.com/after-effects/using/expression-examples.html
// Full Tutorial: https://www.youtube.com/watch?v=I-Acdl_l9G0&t=14s
​
startFade = 500;
endFade = 3000;
try {
C = thisComp.activeCamera.toWorld([0,0,0]);
} catch(err) {
w = thisComp.width * thisComp.pixelAspect;
z = (w / 2)/Math.tan(degreesToRadians(19.799));
C = [0,0,-z];
}
P = toWorld(anchorPoint);
d = length(C, P);
linear(d, startFade, endFade, 100, 0)

Colour

Fill

txt = thisComp.layer("Text").text.sourceText;
c = parseInt(txt,16);
r = c >> 16;
g = (c & 0x00ff00) >> 8;
b = c & 0xff;
[r,g,b,255]/255

Random fill

// Apply on fill
seedRandom(index,true); // change true to 0 for constant change
random([0,0,0,1],[1,1,1,1])

Motion

Wiggle

// frequency by amplitude = how fast by how much
wiggle(20,30);
​
[value[0], wiggle(20,30)[1]] // wiggle y-axis only
​
​
// Smooth wiggle — freq,amp,octave,amp_mult
wiggle(20,30,1,2,time)
​
​
// Jumpy Wiggle 1 (moves at a random FPS) Makes wiggle skip and hold rather than move fluidly.
​
v=wiggle(5,50);
if(v < 50)v=0;
if(v > 50)v=100;
v;
​
// Jumpy Wiggle 2 (moves at a defined FPS)
fps=5; //frequency
amount=50; //amplitude
wiggle(fps,amount,octaves = 1, amp_mult = 0.5,(Math.round(time*fps))/fps);

Inertial Bounce / Overshoot

// Inertial Bounce (moves settle into place after bouncing around a little)
// // Source: https://forums.creativecow.net/docs/forums/post.php?forumid=227&postid=19145&univpostid=19145&pview=t
amp = .05;
freq = 4.0;
decay = 2.0; // springyness
n = 0;
if (numKeys > 0){
n = nearestKey(time).index;
if (key(n).time > time){
n--;
}
}
if (n == 0){
t = 0;
}else{
t = time - key(n).time;
}
​
if (n > 0){
v = velocityAtTime(key(n).time - thisComp.frameDuration/10);
​
value + v*amp*Math.sin(freq*t*2*Math.PI)/Math.exp(decay*t);
}else{
value;
}

Damping

// Source videolancer:https://videolancer.net/expressions/
amp = .04;// The higher the value, the greater the amplitude
freq = 2;// The higher the value, the higher the frequency
decay = 5;// The higher the value, the smaller the delay
​
n = 0;
if (numKeys > 0){
n = nearestKey(time).index;
if (key(n).time > time){
n--;
}
}
if (n == 0){
t = 0;
}else{
t = time - key(n).time;
}
if (n > 0){
v = velocityAtTime(key(n).time - thisComp.frameDuration/10);
value + v*amp*Math.sin(freq*t*2*Math.PI)/Math.exp(decay*t);
}else{
value;
}

Constant move in either x or y axis / Random walker

WIP---
choice = Math.floor(random(0,2));
seedRandom(0,false);
stepx = random(0,50);
stepy = random(0,20);
if(choice ==0){
value + [stepx,0];} else if( choice ==1){
value +[0,stepy];}

Figure of 8

// source: Dan Ebbert
​
center = [thisComp.width,thisComp.height]/2;
radius = 200;
period = 3;
angle = 2*Math.PI*time/period;
x = 2*radius*Math.cos(angle)*Math.sqrt(Math.cos(angle)*Math.cos(angle));
y = 2*radius*Math.cos(angle)*Math.sin(angle);
center + [x,y]

Conical spiral (equidistant)

freq = 60;
mul = 500;
radius = 90;
​
x = radius*Math.sin(time*freq)
y = radius*Math.cos(time*freq)
z = time*mul
value + [x,y,z]

Tessellation

Rectangular

//Create a null layer at the top named "Start Tiles" with a Slider effect "Columns",
//and one at the bottom named "End Tiles"
​
// For Position:
var controlLayer = thisComp.layer("Start Tiles");
var numColumns = Math.floor(thisComp.layer("Start Tiles").effect("Columns")("Slider").value);
var tileIndex = index - controlLayer.index-1;
var columnIndex = tileIndex%numColumns;
var rowIndex = Math.floor(tileIndex/numColumns);
var columnWidth = thisComp.width/numColumns;
var firstTile = thisComp.layer(controlLayer.index+1);
var rowHeight = firstTile.height/firstTile.width*columnWidth;
var xPos = columnWidth*(columnIndex+.5);
var yPos = rowHeight*(rowIndex+.5); [xPos, yPos];
​
// For Scale:
var controlLayer = thisComp.layer("Start Tiles");
var numColumns = Math.floor(thisComp.layer("Start Tiles").effect("Columns")("Slider").value);
var columnWidth = thisComp.width/numColumns;
var firstTile = thisComp.layer(controlLayer.index+1);
var rowHeight = firstTile.width/firstTile.height*columnWidth;
var xyScale = columnWidth/width*100; [xyScale, xyScale]

Hexagonal

height−to−width ratio of regular hexagon=1:1.1547005height-to-width\ ratio\ of \ regular \ hexagon= 1:1.1547005
// POSITION Setup
var controlLayer = thisComp.layer("Start Tile");
var numColumns = Math.floor(thisComp.layer("Start Tile").effect("Columns")("Slider").value); var Mmargins = Math.floor(thisComp.layer("Start Tile").effect("Margins")("Slider").value); var myWidthRatio = thisComp.layer("Start Tile").effect("Height Adjustment")("Slider"); var myZPos = thisComp.layer("Start Tile").effect("Z-Pos Adjustment")("Slider"); var mySeed = thisComp.layer("Start Tile").effect("Seed")("Slider"); // Indexing var tileIndex = index - controlLayer.index-1; var columnIndex = tileIndex%numColumns; var rowIndex = Math.floor(tileIndex/numColumns); if (rowIndex % 2 == 0) {var oddEven = 1;}else{oddEven=0;} // Z-Positioning seedRandom(index+mySeed, true); var zPos = random(-myZPos,myZPos);
​
// Positioning
var columnWidth = thisComp.width/numColumns ;
var firstTile = thisComp.layer(controlLayer.index+1);
var rowHeight = firstTile.height/firstTile.width*columnWidth;
var xPos = columnWidth*(columnIndex+.5);
var yPos = rowHeight*(rowIndex+.5);
[xPos +oddEven*columnWidth/2, yPos*myWidthRatio,zPos];
​
​
//
// SCALE Setup
var controlLayer = thisComp.layer("Start Tile");
var numColumns = Math.floor(thisComp.layer("Start Tile").effect("Columns")("Slider").value);
var myScaleRatio = thisComp.layer("Start Tile").effect("Scale Adjustment")("Slider");
​
// Indexing
var columnWidth = thisComp.width/numColumns ;
var firstTile = thisComp.layer(controlLayer.index+1);
var rowHeight = firstTile.width/firstTile.height*columnWidth;
​
​
//Scaling
var xyScale = columnWidth/width*100;
[xyScale, xyScale]*myScaleRatio;

Responsive Systems / Setups

Dynamic-sizing text box / lower-third (depreciated, see new code)

// Create two text layers named "DATA1" & "DATA2"
// Variables
pad = 50;
xPad = 15;
yPad = 15;
​
seed = 1 // allow you to change between two text layers
​
// Making it work
if(seed==1){
src = thisComp.layer("DATA1")
}else {
src = thisComp.layer("DATA2")
};
​
box = src.sourceRectAtTime(time-src.inPoint);
[box.width + pad + xPad, box.height+pad +yPad];

Slider with center fade on

v = transform.position[0] // x
d = length(v,thisComp.width/2);
maxDist= 960;
​
ease(d,0,maxDist,100,0)
/* There needs to be two layers named 'startCard' & 'endCard'
This allows rotation value to dynamically change when layers are inserted or removed.
All duplicates need to be placed within these two layers
*/
​
//anchor point
radius = 500
value+[0,0,radius]
​
// Y rotation
​
startIndex=thisComp.layer("startCard").index;
endIndex = thisComp.layer("endCard").index;
numpt = startIndex-endIndex+1; // total number of layers
myIndex = index-startIndex;
angle = 360/numpt
​
myIndex*angle+

Linking layers' rotation with opacity

Using distance & vector position

// Make sure there is a camera
​
​
//Opacity
startVal = 0;
endVal = 100;
fadeAngle = 180;
​
v = toCompVec([0,0,1]); // layer position to comp position in Z
d = length(toWorld(anchorPoint),thisComp.layer("Camera 1").toWorld([0,0,0]));
c = v[2]/d;
ease(c,Math.cos(degreesToRadians(fadeAngle)),1.0,startVal,endVal)
​

Using rotation

angle = transform.yRotation%360;
minAngle = 0;
maxAngle = 360;
mid = (maxAngle+minAngle)/2;
if (angle < mid)
linear(angle,minAngle,mid,100,0)
else
linear(angle,mid,maxAngle,0,100)

Time Remapping / Playback

Random frame playback

// Source Dan Ebbert
​
fr = 12; // frame rate;
​
numFrames = 8;
​
seedRandom(index,true);
​
seg = Math.floor(time*fr);
​
f = Math.floor(random(numFrames));
​
for (i = 0; i < seg; i++)
​
f = (f + Math.floor(random(1,numFrames)))%numFrames;
​
framesToTime(f);

Random delayed/advanced layer playback

seedRandom(index,true);
myDelay = random(1,10);
time + myDelay;

Play one frame at a time (without 'float time')

fps = 1;
x = framesToTime(Math.round(time*fps))
//There probably is a better way but I cannot figured it out right now.

Random still frame (Good for spriting)

//Apply this to a composition with a image sequence inside
seedRandom(0,true);
t= random(0,72);
framesToTime(t);

Playback at every nth frames

n = 3 // frames
time*n

Hold and play every nth frames [source]

n = 3; // play every __ frames
m = 3; // hold each frame ___ times
​
f= timeToFrames(timeRemap);
​
p=Math.floor(f/m);
​
framesToTime(p*n);

Triggering animations with marker

Workflow

  1. Create a preComp with all the different types of animation

  2. In the main Comp, put an expression on the time remapping of that preComp

  3. Add markers to trigger animations

  4. Learn more

//https://gist.github.com/animoplex/cecf1c64aec4f2733ecd0edbebf4786d

Staggering animations

// Copying animation of a layer, and start it at inPoint of layer
thisComp.layer(index+1).transform.position.valueAtTime(time-inPoint)
​
// Using delay
offset = 1;
thisComp.layer(index+1).transform.position.valueAtTime(time-offset)

Radial scale up staggering

// VARIABLES + SETUP
target = thisComp.layer("master").transform.scale;
maxDist = 1000;
maxDelay = 1;
​
dst = length(transform.position,[thisComp.width/2,thisComp.height/2]);
delay = linear(dst,0,maxDist,0,maxDelay);
​
// EXECUTION
target.valueAtTime(time-delay)

Effector opacity fade on

//Create a null named "EFFECTOR"
//Add 2 Slider Control Effects renamed to "maxDist" and "delay"
​
​
// EXECUTE
target = thisComp.layer("EFFECTOR").transform
maxDist = thisComp.layer("EFFECTOR").effect("maxDist")("Slider");
maxDelay = thisComp.layer("EFFECTOR").effect("delay")("Slider");
​
dst = length(transform.position, target.position);
delay = linear(dst, 0, maxDist, 0, maxDelay);
​
// EXECUTION
target.opacity.valueAtTime(time - delay)

Masks & Paths

Looping paths (loopOut() doesn't work for paths)

Pingpong
cycle
Pingpong
if (numKeys >1 && time > key(numKeys).time){
t1 = key(1).time;
t2 = key(numKeys).time;
span = t2 - t1;
delta = time - t2;
seg = Math.floor(delta/span);
t = delta%span;
valueAtTime((seg%2) ? (t1 + t) : (t2 - t));
}else
value
cycle
if (numKeys >1 && time > key(numKeys).time){
t1 = key(1).time;
t2 = key(numKeys).time;
span = t2 - t1;
delta = time - t2;
t = delta%span;
valueAtTime(t1 + t)
}else
value

Drawing

square
Circle
archimedes
square
vertices = [[0,0], [100,0], [100,100], [0,100]];
inTangents=[]
outTangents=[]
closed = true;
createPath(vertices,inTangents, outTangents, closed);
Circle
//sliders
w = 500;
​
r = w/2;
ratio = .5523;
t = r*ratio;
vertices = [[r,0],[0,r],[r,2*r],[2*r,r]];
inTangents = [[t,0],[0,-t],[-t,0],[0,t]];
outTangents = [[-t,0],[0,t],[t,0],[0,-t]];
closed = true;
createPath(vertices,inTangents,outTangents,closed);
​
archimedes
let w = effect("w")("Slider")
let r = w/2;
​
let points = effect("points")("Slider");
let steps = effect("steps")("Slider");
let step =(Math.PI*2)/ steps;
let angle = effect("angle")("Slider");
let radius = 0;
let vertices =[];
​
for(i =0; i<points; i++){
angle += step;
radius = i/ points*r;
x = Math.cos(angle)*radius;
y = Math.sin(angle)*radius;
vertices[i]=[x,y];
}
​
createPath(vertices,[],[],0)

​