Contents
- Introduction
random
- Creating An Array
while
Loop- Applying It To Things Other Than Numbers
- Conclusion
Introduction
Sometimes you just need a random number. But not that random. I find that, when having numbers flash randomly on the screen for a video effect, that it works best when there are no repeats in the sequence. This can happen from time to time, especially if your range isn't that large, when using the random()
function. While it is perfectly understandable for what the function does, it still makes our animation look a little staggered.
So I set myself the challenge of writing an expression that excludes the previous number from the random generation, and thought about how this could be applied to projects in the future.
Here's what I did.
random()
Let's start at the beginning. If you need a random number in After Effects, using the random(minVal, maxVal)
function is a great place to start. By inputting a minimum and maximum value into the function, After Effects will generate a random number from within those bounds every frame.
random(1, 11)
That's a lot of decimal places! Since I just want integers, I use Math.floor
to round the numbers down. Because the range of the random
function is 1 to 11, this method will produce a random number between 1 and 10, since the top of the range is not included.
Math.floor(random(1, 11))
Much better. However I want to control how often the number changes, not on every frame. To do that, we need to use the seedRandom(seed, timeless?)
function. This function requires 2 arguments, the seed, and whether or not timeless mode is set to true or false. By default, timeless is set to false, meaning that the random
function will generate a new number each frame. However, if we set this to true, the random
function will only generate a number once.
seedRandom(0, timeless = true);
Math.floor(random(1, 11))
Now that the number only generates once, I can manipulate the seed number using a simple for
loop to control how often the number generates. If you're unfamiliar with for
loops I've written up an article about them in depth here.
Here is my basic loop:
let counter = 0;
let num = 0;
for (i = 0; time >= i; i++) {
seedRandom(counter, timeless = true);
num = Math.floor(random(1, 11));
counter++
}
num
First, I set up the variables counter
to track the changes in time, and num
, to store my random number. The loop is set to add 1 to i
whenever time >= i
, meaning the loop updates every second the timeline is active. Everytime the loop refreshes, seedRandom
is updated by the change in counter
's value, producing a new random number when num
is called at the end of the expression.
By adjusting i
in the for
loop, we can control how often the number changes per second. For instance, if we were to change the argument to time >= i/2
, the number will change twice every second instead of once.
Now that we have an expression which allows us to generate a new random number at an interval of our choosing, it needs to be amended to ensure we never have any duplicate entries one after another.
Creating An Array
In order for After Effects to be able to compare numbers, the numbers need to be saved somewhere for After Effects to review. So I decided to save my random numbers to an array as they are generated over time.
let counter = 0;
let num = 0;
let allNum = [];
for (i = 0; time >= i/2; i++) {
seedRandom(counter, timeless = true);
num = Math.floor(random(1, 11));
allNum.push(num);
counter++
}
allNum
I create the variable allNum
outside of my for
loop, and then push()
the number currently inside of our num
variable to the array. This saves each number as it is generated across the timeline.
By calling allNum
at the end, we are able to see all the numbers in our array by the end of our timeline:
As you can see, this is less than ideal with a set of double 7s and triple 1s in our sequence. If we called num
and played our timeline, the doubles could make it look as if our timeline had frozen or stopped animating. Therefore, we need to ensure that our expression takes into account the previous entry of this array, and excludes it from being added again in such quick succession.
while
Loop
I found the answer to this was to add a while
loop within the existing for
loop.
A while
loop allows you to specify a command while certain criteria is true. In this case, we want it to be active while the current value of num
matches the previous value, listed in our allNum
array.
while (num == allNum[counter - 1]) num = Math.floor(random(1, 11));
While the current value of num
matches the previous entry inside of allNum
, the while
loop will generate a new random number using a new iteration of Math.floor(random(1, 11))
. This loop remains active as long as the numbers stay the same. Therefore, until a different number is generated, the while
loop will prevent the same number from being added to the allNum
array.
The while loop is inserted into our for
loop like so:
let counter = 0;
let num = 0;
let allNum = [];
for (i = 0; time >= i/2; i++) {
seedRandom(counter, timeless = true);
num = Math.floor(random(1, 11));
while (num == allNum[counter - 1]) num = Math.floor(random(1, 11));
allNum.push(num);
counter++
}
allNum
It is added after num
has attempted to generate a new number with the update to seedRandom
, but before it is pushed to the array so it can be re-generated if necessary.
With this amend, our array now looks like this:
As you can see, we no longer have double 7s or triple 1s in our sequence.
Therefore, if we call num
at the end of our expression instead of allNum
, we will have a random number between 1 and 10, excluding the previous entry generated, twice every second. Hooray!
Applying It To Things Other Than Numbers
While random numbers are all well and good, there are other uses for this expression too. For instance, what if we wanted to randomise words on the screen instead, without duplicate repeats?
All we need to do is add another array.
var ranText = ["Banana", "Apple", "Pear", "Peach", "Pulm", "Orange", "Mango", "Lychee", "Pinapple", "Dragonfruit"];
Here is an array housing the text we want randomised. There are 10 entries, matching the range of the number generator we made previously.
By calling an entry of our new array using our num
variable, we can call the entry whose index matches that number:
//Text
var ranText = ["Banana", "Apple", "Pear", "Peach", "Pulm", "Orange", "Mango", "Lychee", "Pinapple", "Dragonfruit"];
//variables
let counter = 0;
let num = 0;
let allNum = [];
//For loop
for (i = 0; time >= i/2; i++) {
seedRandom(counter, timeless = true);
num = Math.floor(random(1, 11));
while (num == allNum[counter - 1]) num = Math.floor(random(1, 11));
allNum.push(num);
counter++
}
//return
ranText[num - 1]
Useful! Just remember to make sure that your num
variable at the end includes -1
, to account for the array index starting at 0.
Instead of using an array, it is possible to connect your expression to data inside of a .csv file. I go into how this is possible in my kinetic text breakdown here, should you want to make a template that is constantly updatable.
However you can use it in other ways too. Images can be randomly displayed as well, by connecting this expression to a slider controlling the frames of a precomp.
Conclusion
In conclusion, it is possible to add a little bit more control over random numbers in order to make them look a little more aesthetically pleasing to the eye.
What did you think of this method? Is there an easier way? Did this help you with your project in any way? Please leave a comment and let me know.
Top comments (0)