Generating Random Motion Using AE 5.5’s Expressions
Originally published in 2002
In this tutorial, Dan Ebberts demonstrates a method of generating motion that is random in both time and space and allows you to quickly assemble a composition with a pleasantly fluid, chaotic movement.
Project File: .sit (currently unavailable)
The Problem:
The concept behind this tutorial came out of an online discussion. A fellow After Effects enthusiast was trying to come up with an expression to generate motion that was random in time and space. That is, target positions would be random as would the time it takes to get there. The problem that you encounter when trying to develop such an expression is that there just doesn’t seem to be a good way to carry the value of variables forward in time from one frame to the next. Any variables that you define will be gone at the next frame. Consider the following example expression for opacity:
if (time = 0){
x = 10
}
else{
x = x + 1
}
This works fine at the first frame. At the second frame you get an error because x is undefined. The expression doesn’t remember that x was 10 on the previous frame. This becomes a serious limitation when you are trying to do something over a random amount of time because once you decide how long it should take, you need to check at every frame to see if you’re done yet. Hard to do if you can’t pass that value from one frame to the next.
Seed_random To The Rescue:
It turns out that you can get around this problem with the enhanced (for AE 5.5) seed_random function. This function allows you to establish a repeatable sequence of random numbers and regenerate them at will, just by restoring the seed to a previous value and then calling the random() function. This gives us a solution to the problem because even though we can’t pass our random values from one frame to the next, we can recreate them using seed_random. Let’s look at how seed_random works.
Open the project file and then open the “basic comp”. This is just a 640×480 comp with a 50×50 solid layer added. Select “Solid 1” and reveal the position property. Alt-click (Option-click) on the stopwatch to activate the expression for this property. Enter the following expression:
[random(0,this_comp.width),random(0,this_comp.height)];
This will cause the square to jump to a random position in the comp. Preview the comp. You’ll notice that the square jumps to a new position on every frame. Not very useful. Add a new line to the expression so that it now reads:
seed_random(1,true);
[random(0,this_comp.width),random(0,this_comp.height)];
Notice that again the square jumps to a random position but when you preview it stays there. This new behavior is due to a change that Adobe made to the seed_random function for AE 5.5. They’ve added a second parameter, that when set to “true” sets the random function so that random numbers generated don’t depend on time. The random numbers depend on the layer number, the property number, the value of the first parameter in the seed_random call (the “seed”), and the number of times the random function has been called. That means that the same expression will generate different random numbers in a different layer or property (later we will take advantage of this to quickly add complexity to our comp). Try changing the “seed” parameter to some other number. You’ll notice that the square moves to a different location and stays there. Change the second parameter to “false” and notice that when you preview, the square jumps all over the place again.
Add a little more code to your expression so that it looks like this:
if (time < 1){
seed_random(1,true)
}
else{
seed_random(2,true)
}
[random(0,this_comp.width),random(0,this_comp.height)];
Now you’ll notice that when you preview, the square stays at one location for the first second and then jumps to another location for the remainder of the comp.
So how do we use this new, more powerful seed_random function? Here’s the key concept: With the second parameter set to “true”, for a given layer, property, and seed value, the random number you get when calling the random() function only depends on how many times you have called the function since setting the seed. That is, the first time you call random() after setting the seed you will get a particlar random number (let’s call it x). The second call will generate a second random number (let’s call this one y) and so on. But if you call seed_random again with the original seed, the sequence will start over. That is, the next random() call will regenerate the original random number (x in this case). Using a different seed value will cause a completely different sequence to be generated. Consider the following example:
seed_random(1,true); //set the seed value
A=random();
B=random();
C=random();
seed_random(1,true); //reset the seed to the same value
I=random();
J=random();
K=random();
seed_random(2,true) //new seed
Q=random();
R=random();
S=random();
seed_random(1,true); // reestablish original seed
X=random();
Y=random();
Z=random();
In this case, A,B, and C will all be different random numbers. However, A and I will be the same, B and J will be the same, and C and K will be the same because the seed has been reset (to 1 in this case) and the random sequence starts over. Q,R and S will be a new sequence of random numbers because the seed has been changed to 2. The sequence X,Y,Z will be the same as A,B,C and I,J,K because the seed has been set to 1 again. So we can return to a sequence of random numbers by reestablishing the seed value that generated those numbers.
How does this help us? To generate our random motion, at any given frame we need to know several things about the current segment of motion: start time, start position, end time, and end position. These will all be random numbers that we will generate using a different seed value for each segment of motion. And we will base the seed value on which segment we’re currently in. So all we have to do is have the expression be able to figure out which segment it’s in (from which it will be able to figure out the seed value), set the seed, and regenerate the sequence of random numbers that give us the start time, start position, end time, and end position values for the current segment.
OK – let’s review what our goal will be for this expression. We want the square to move smoothly from one random position on the screen to another. We also want the time it takes to do this to be random (let’s say between .5 and 2 seconds). This expression will do that:
seg_start_time = 0; //initial conditions
seg_end_time = 0;
i =1;
tmin = .5; //minimum cycle time (can’t be zero)
tmax = 2; //maximum cycle time
while (time >= seg_end_time){
i = i+1; seed_random(i,true);
seg_start_time = seg_end_time;
seg_end_time = seg_end_time + random(tmin,tmax);
}
percent = (time – seg_start_time)/(seg_end_time – seg_start_time);
target_x = random()*this_comp.width;
target_y = random()*this_comp.height;
seed_random(i-1,true);
x=random(); //this is a throw-away value
old_x = random()*this_comp.width;
old_y = random()*this_comp.height;
ease(percent,[old_x,old_y],[target_x,target_y]);
Copy this expression and paste it into the position expression (or just open “basic comp + expression”). Preview the comp. You’ll notice that the square now has a nice, fluid, motion that is random in duration and direction.
In this expression, “i” is our segment counter, which we’ll also use as the seed value for that segment. “tmin” and “tmax” define the range of random times possible for the duration of each segment. In this case, a segment will last between .5 and 2 seconds. The “while” loop is where the expression figures out which segment it’s in. It does this by incrementing the seed and accumulating random segment durations until a value greater than the current time is reached. At this point it knows the start, end, and current time of the current segment so it can calculate the percentage of this segment that is completed. It calls the random() function two more times to get the target x and y coordinates for the end position of this segment. The next part is a little tricky. The expression still needs to know the start position of this segment so it can calculate (based on the percentage completed) the current position. Since the start position is the same as the end position of the previous segment, we just decrement the seed to return to the seed of the previous segment. The expression then calls the random() function to get end x and y coordinates of the previous segment. You’ll notice that there is one “throw-away” call to random() before the previous x and y values are determined. This is because for each seed, the first random() call is always used for the end time (which we already know) – the 2nd and 3rd calls are used for x and y coordinates.
Open the “expression comp”. This comp is the same as the previous comp except for the addition of similar randomizing expressions for opacity and rotation. The opacity expression is:
seg_start_time = 0; //initial conditions
seg_end_time = 0;
i =1;
tmin = .5; //minimum cycle time (can’t be zero)
tmax = 2; //maximum cycle time
while (time >= seg_end_time){
i = i+1; seed_random(i,true);
seg_start_time = seg_end_time;
seg_end_time = seg_end_time + random(tmin,tmax);
}
percent = (time – seg_start_time)/(seg_end_time – seg_start_time);
target_opacity = random()*100;
seed_random(i-1,true);
x=random(); //this is a throw-away value
old_opacity = random()*100;
ease(percent,old_opacity,target_opacity);
and the rotation expression is:
seg_start_time = 0; //initial conditions
seg_end_time = 0;
i =1;
tmin = .5; //minimum cycle time (can’t be zero)
tmax = 2; //maximum cycle time
while (time >= seg_end_time){
i = i+1; seed_random(i,true);
seg_start_time = seg_end_time;
seg_end_time = seg_end_time + random(tmin,tmax);
}
percent = (time – seg_start_time)/(seg_end_time – seg_start_time);
target_rotation = random()*360;
seed_random(i-1,true);
x=random(); //this is a throw-away value
old_rotation = random()*360;
ease(percent,old_rotation,target_rotation);
These expressions are virtually identical to the one for position except that they have been modified to generate a range of values appropriate for opacity and rotation. Preview the comp and notice that the square now has random rotation and fade as well as movement.
Here comes the fun part. Duplicate “Solid 1” a bunch of times (or just open “multi comp”) and preview the comp. Remember – you can do this really quickly by using the Control+D shortcut (Command+D on the Mac). Now when you preview there’s a flurry of activity. The random numbers for each layer are different even though the expressions are exactly the same for each.
Open “fun comp” and preview it. For this comp, I duplicated “Solid 1” two times and then replaced each of the solids with one of the Illustrator files. Then I duplicated each of the layers a bunch of times. You can see that once you have the expressions set up, duplicating layers quickly adds complexity to your comp.
You can dissect the expressions to see how they work, or you can just copy and paste them into your own comps. The three examples I’ve given (position, opacity, and rotation) should give you a pretty good idea of how to tailor the expression for other properties. I hope this inspires some of you to play around with these and create something really cool!
Enjoying the news? Sign up for the Creative COW Newsletter!
Sign up for the Creative COW newsletter and get weekly updates on industry news, forum highlights, jobs, inspirational tutorials, tips, burning questions, and more! Receive bulletins from the largest, longest-running community dedicated to supporting professionals working in film, video, and audio.
Enter your email address, and your first and last name below!
Responses