basis

Randomness is one of the most frequently asked questions in the use of expressions, and After Effects gives us a great library of tools to implement it. The basic Random () method has several different styles. The following example may help you understand the different ways in which random() can be called:

Random Expression               Result

random()                 // number between 0 and 1
random(6)                // number between 0 and 6
random(-2.4)             // number between -2 and 4
random([3.4.5])          // vector between [0,0,0] and [3,4,5]
random([3.4.5], [6.7.8])  // vector between [3,4,5] and [6,7,8]
Copy the code

You can see how flexible the random() method is.

We’ll show you how to use it with several examples. Suppose you want to Position a layer randomly in composition, any of the Position expressions below will do the trick:

random([thisComp.width,thisComp.height])

random([0.0],[thisComp.width,thisComp.height])

x = random(thisComp.width);
y = random(thisComp.height);
[x,y]

x = random()*thisComp.width;
y = random()*thisComp.height;
[x,y]
Copy the code

While the above expressions do randomly locate layers, if you try to use them, you’ll find a serious limitation of the Random () method. Each frame has a different result. If you want to have a layer jumping around like crazy, then this is fine. But for the most part, what you need is more controlled randomness. This is where seerandom() comes in.

Random seed remedies

SeedRandom () is a clever approach from Adobe that allows us to control randomness. In fact, it serves more than one purpose. You can also use it to change the sequence of random numbers generated by random(). SeedRandom () takes two arguments. The first is a random seed and the second is a true/false flag that tells After Effects whether the random number generated by random() will be “eternal”. We’ll get back to eternity in a minute, but first let’s look at another parameter. In its simplest form, seedRandom is simply a seedRandom number generator to produce a specific sequence of random numbers that changes with each frame.

For example, these two expressions will generate completely different sequences of random numbers (which change with each frame):

seedRandom(3);
random()

seedRandom(999);
random()
Copy the code

All right, here’s the cool part. By setting the second argument to seedRandom() to “true”, the number generated by Random() is the same in every frame. Here is an example of a Position expression that moves a layer to a random Position and holds it there:

seedRandom(3.true);
random([thisComp.width,thisComp.height])
Copy the code

Remember, if seedRandom() is not called, the layer will pop to a different position on each frame. How does that help us? Getting a layer to move to a random location and stay there is pretty boring, right? Yes. But to get a new position, we just need to change the seed. Therefore, in order for the layer to move randomly in an orderly manner, all we need to do is update the seed periodically and get a new random position from Random ().

Let’s build an example. Suppose we want our layer to move to a new random position every half second. We just need to add some new code to the expression, changing the seed every half second. How do we do that? Remember that the JavaScript function math.floor () rounds to the nearest integer. We can use it to generate seeds that change every half second by dividing the current time by 0.5 and rounding to the nearest integer. Here’s an example (you can change the value of “holdTime” to change the length of time each position is held):

holdTime = . 5; //time to hold each position (seconds)

seed = Math.floor(time/holdTime);
seedRandom(seed,true);
random([thisComp.width, thisComp.height])
Copy the code

Now, this layer is still moving randomly, but every half second. Let’s make a change so that our movement stays in the synthesized “title safe” area, which we’ll do by limiting the result of the random() call to between 10% and 90% of the synthesized width and height.

holdTime = . 5; //time to hold each position (seconds)
minVal = [0.1*thisComp.width, 0.1*thisComp.height];
maxVal = [0.9*thisComp.width, 0.9*thisComp.height];

seed = Math.floor(time/holdTime);
seedRandom(seed,true);
random(minVal,maxVal)
Copy the code

The effect is shown in figure

(Note that there is a slightly simpler approach that uses a combination of the posterizeTime() method and seedRandom(), but we’ll look at it when we delve into how to manipulate time using expressions).

Ok, but what if we want layers to move smoothly from one position to another? This brings us to one of the most important concepts for generating random motion, which is that random() generates the same random sequence whenever the seed is set to a particular number. For example, if we set the seed to 1, Random () might give.45633. If we replace the seed with a 2, we might get.78341. But if we change the seed back to 1, we get.45633 again. So how does this help us produce smooth random motion? Let’s look at the example above again. In the first half second, the seed is counted as 0. Therefore, for each frame in the first half second, calling random() produces the same random Position based on a seed of 0. After the first half second, the seed becomes 1, randomly changing position. So what if we “peek ahead” to see what the next position will be, and start heading there, by changing the seed to the value we’ll use in the next half second? The sequence computes the seed for this half second, uses it to get the random position for this half second (which will be our starting position), increments the value of the seed by one, and uses it to see what the random starting position will be in the next half second (which we will use as the ending position for the current half second fragment).

Here’s the code:

segDur = . 5;// duration of each "segment" of random motion
minVal = [0.1*thisComp.width, 0.1*thisComp.height];
maxVal = [0.9*thisComp.width, 0.9*thisComp.height];

seed = Math.floor(time/segDur);
segStart = seed*segDur;
seedRandom(seed,true);
startVal =  random(minVal,maxVal);
seedRandom(seed+1.true);
endVal = random(minVal,maxVal);
ease(time,segStart,segStart + segDur, startVal, endVal);
Copy the code

You’ll notice that in the demo we now have multiple stars, all in different random positions. They all have the same copy of the random position expression, so you may be wondering why they have different positions. The result is that for any given random seed, the generated random values are unique to each layer. Inside, After Effects somehow modifies the seed so that it’s in each layer, composition, properties, Effects and you’ll notice that in the demo movie we now have multiple stars, all in different random positions. They all have the same copy of the random position expression, so you may be wondering why they have different positions. The result is that for any given random seed, the generated random values are unique to each layer. Internally, After Effects modifies the seed in a way so that it is unique to each layer, composition, properties, Effects, etc. In most cases, this is a very convenient feature, but in some cases it’s nice to be able to force two different layers to generate the same random number.

Anyway, to review, the seed is derived from time and segment duration and becomes our “segment number,” where segment 0 starts at time = 0, segment 1 starts at time =.5, and so on. Once we have the seed, we seed it as a random number generator and call random() to get the starting position of the fragment. We then give the seed + 1, seed the random number generator with the new seed, and call random() again to get the starting position of the next fragment (which, of course, is the ending position of the current fragment). We then use ease() to interpolate between the two positions so that the layers move from one position to the next. You can change the interpolation from ease() to Linear () to get a different effect.

That’s all you need to generate basic random motion in After Effects. Ah, if only life were that simple.

If you duplicate the layer a few times and preview the composition, you will see the layer move to different positions, but all layers move in sync. That is, they all arrive at their new destinations at the same time. This can be a useful effect, but what if you want something a little messier? Let’s see what you can do to solve this problem.

Effect of synchronization

More chaotic effects

One solution is very simple and gives very useful results. The other solution is much more complex (in code and theory), but it does open up endless possibilities for randomness in After Effects. The first is the simple solution. We can modify the expression so that the duration used by each layer is a random number within a given range, and each layer is different. Here is the modified code:


segMin = 3.; //minimum segment duration
segMax = 7.; //maximum segment duration
minVal = [0.1*thisComp.width, 0.1*thisComp.height];
maxVal = [0.9*thisComp.width, 0.9*thisComp.height];

seedRandom(index,true);
segDur = random(segMin, segMax);
seed = Math.floor(time/segDur);
segStart = seed*segDur;
seedRandom(seed,true);
startVal =  random(minVal,maxVal);
seedRandom(seed+1.true);
endVal = random(minVal,maxVal);
ease(time,segStart,segStart + segDur, startVal, endVal);
Copy the code

As you can see, the only change from the previous version is the code at the beginning, which uses the layer’s index SEED random number generator and then calls random() to get the fragment duration. This simple change can add some confusion to the mix.

However, if you watch its movement carefully, you will begin to notice that each movement of any given layer always takes the same amount of time. What if all you really want is for every action to be random? This presented us with a major obstacle, and we had to come up with a new, very powerful concept to get around it. Before we can solve it, we need to do a little interlude.

Expressions have no memory

The implementation of expressions in After Effects is stateless. Here’s the good news: this allows you to move the time marker to any synthesized frame and After Effects can immediately start displaying your frame because it doesn’t need to know what any expression application has done in the past. So given any frame, After Effects simply evaluates the expression and applies the result to the value of the property without the expression (that is, the original value of the property plus the effect of any keyframe). That means a huge impact. An example might be helpful.

Suppose you want to add a random value from 0 to 10 degrees to the layer rotation for each frame. You might expect sporadic movement, but you might also expect the rotation of the layers to always increase clockwise. The code seems simple enough. It looks like this should work:

rotation + random(10)
Copy the code

Results the following

When we look at the results, it is clear that After Effects does not accumulate the Effects of the random() function from frame to frame. At each frame, it starts again from the original value of the rotation (that is, 0) and adds a random number. This is obviously not what we wanted or expected. What do we do?

Unfortunately, in this case, what we often need to do is add up frame by frame by code added to the expression. In other words, our expression must recalculate everything that has happened since frame zero. To do this, we have to use some JavaScript to construct a “while” loop that passes one frame at a time in the composition and adds a random number. The code for this loop looks a little complicated, but after you execute it a few times and you start to get a feel for what you’re doing, it gets easier.

Here’s what the code looks like:

j = 0;     //initialize loop counter
accum = 0; //initialize random accumulator
seedRandom(index,true)

while (j < time){
  accum += random(10);
  j += thisComp.frameDuration;
}

rotation + accum
Copy the code

The effect of accumulating loops

Now we have what we wanted. This is done by the “while” loop, which iterates through the code between curly braces until the variable “j” is greater than the current time. Each time through the loop j will increase the time value by one frame (thiscomp.frameduration). When the loop ends, the variable “Accum” contains the sum of all the random numbers generated for this frame and all the previous frames. While the code isn’t terribly complex, it all comes at a cost. At each frame, the loop must revisit all previous frames. As your compositing length increases, render time may start to stagnate as the expression has more and more to do on each frame. So there are some practical limitations to the use of this technique, but for most cases (short compositions) it works just fine. In fact, as you will soon see, many applications of this technique do not require single-frame granularity and are less costly in terms of rendering time. What I mean by that is that you end up traversing the composition in larger chunks of time than a single frame, which further extends the usefulness of the technique. You’ll see.

Go back to the random motion effect

Ok, so let’s go back to the random duration of random motion. In this process, we will eventually use the circular computing technique we have just studied. So let’s see what we need to do. We want the layer to move smoothly from one position to the next, and we want this to happen in a random amount of time (within the range we will define). With “seedRandom()” and “while”, we can handle this problem. First, let’s look at the final code:

segMin = 3.; //minimum segment duration
segMax = 7.; //maximum segment duration
minVal = [0.1*thisComp.width, 0.1*thisComp.height];
maxVal = [0.9*thisComp.width, 0.9*thisComp.height];

end = 0;
j = 0;
while ( time >= end){
  j += 1;
  seedRandom(j,true);
  start = end;
  end += random(segMin,segMax);
}
endVal =  random(minVal,maxVal);
seedRandom(j-1.true);
dummy=random(); //this is a throw-away value
startVal =  random(minVal,maxVal);
ease(time,start,end,startVal,endVal)
Copy the code

Random duration effect

Let’s see what has changed from the previous version. The main difference is in the setup and execution of the “while” loop. A couple of things happened here. The “while” loop is where the length of each movement “fragment” is determined. Segment length is a random number between segMin and segMax (. In this case, 3 seconds and 0.7 seconds). We will sum the lengths of random fragments by traversing the composition, starting at time 0, until our end reaches the current time. So, for our purposes here, we don’t need to revisit each frame every time we run the expression — we just need to look at the beginning of each fragment (on average, about every 0.5 seconds or so). At the same time, we increment each fragment by “j”. This makes the “j” unique for each fragment and a perfect candidate for the seed of “seedRandom()”. So when we exit the loop, we know the start and end times of the current segment (” start” and “end”), and we have seeds (“j”) to generate the end times. Outside of the loop (after the close curly brace), we know that the random number generator is still seeded by the index (“j”) of the current segment. So we call random() again to get the end of the fragment. Now we have everything we need (“start”, “end”, and “endVal”) except the start position. We can get it by backing up to the previous seed, re-seeding the random number generator, discarding the first random number, and retrieving the starting position (” startVal “). Now we just need to apply the same ease() statement as before to generate our random duration, smooth random motion.

Everything is much easier from now on because it’s just a change of theme. In the next section, we will extend these concepts to randomize other attributes.