
Building the World’s Greatest Cameraman Using After Effects 5.5 Expression Controls
Originally published in 2002
With version 5.5 of After Effects, Adobe added the wonderful world of the powerful new Expression Controls. In this tutorial, Dan Ebberts looks at several different ways that expression controls can be useful in your projects.
This is an Advanced Tutorial.
introduction
In this tutorial we’re going to explore the wonderful world of the powerful new expression controls which Adobe introduced with After Effects 5.5. We’ll look at several different ways that expression controls can be useful in your projects. First we’ll examine the most straight-forward use of expression controls, which is to use them as a manual adjustment “master” control to help you set up a comp just the way you want it. Then we’ll explore using expression controls as master animation controllers. Next we’ll examine how you can use an expression control as a limited type of global variable to distribute information to expressions in other layers. Finally, we’ll have some fun using expression controls to create the world’s greatest cameraman. That’s a lot of ground to cover, so let’s get moving!
the basics

After Effects 5.5 introduced six new expression controls: slider control, angle control, checkbox control, color control, point control, and layer control. These controls are applied as effects and are located in the “Expression Controls” group in the effect menu. When using these for global animation control, I like to apply them to a null layer (and then turn off the visibility of the null so it doesn’t clutter the comp window) so it will be easy to find them later, but you can apply them to other layers as well. The first control we’re going to look at is the slider control.
slider control

Go ahead and open the project file and select the tab for the “slider” comp. This comp contains a null layer (to which a slider control has been applied), a simple Illustrator graphic layer that has been duplicated a bunch of times, and a camera. Notice that the graphic layers are 3D because we’ll be using the slider control to position the layers in “Z” space. Select the null layer and hit Ctrl+Shift+t (PC) or Cmd+Shift+t (Mac) to reveal the “Effect Controls” palette. Select one of the graphic layers and type “p” to reveal the position property. Click on the little twirly to reveal the following expression:
position+[0,0,(index-1)*this_comp.layer(“Null 1”).effect(“Slider Control”).param(“Slider”)]
This expression establishes each layer’s position in “Z” space based on its layer number (index) minus one, times the value of the slider. So as the value of the slider increases, the layers will spread themselves out along the z axis. Go ahead and scrub the value of the slider (by clicking on the value and dragging). Pretty slick, huh? Notice that if you drag the value negative, the layers spread out in the negative z direction. You can also adjust the value by clicking on the little twirly next to “Slider” and dragging the slider. One other thing to note is that if you right-click (PC) or Control-click (Mac) on the value and select “Edit Value” from the context menu, you can adjust the range of the slider. You might want to do this to give yourself a wider range or more precision with the slider.
Note that setting up a comp like this is fairly simple. The graphic layers are all exactly the same. Once you get the expression set up you can quickly create a bunch of duplicates by typing Ctrl+d (PC) or Cmd+d (Mac) and the expression will automatically accommodate any number of layers.
angle and color controls
Select the tab for the “angle and color comp”. This comp contains a null and multiple copies of a graphic layer that has had its anchor point moved to the bottom and had the “tint” effect applied. Select the null layer and hit Ctrl+Shift+t (PC) or Cmd+Shift+t (Mac) to reveal the “Effect Controls” palette. You’ll notice that the null has an angle control and two color controls applied. The color controls have been renamed “start_color” and “end_color” so that we can refer to them by name in our expressions. Select one of the graphic layers and type “r” to reveal the rotation property and click the little twirly to reveal the following expression:
index*this_comp.layer(“Null 1”).effect(“Angle Control”).param(“Angle”)
This expression calculates the layer’s rotation by multiplying the layer number (index) by the value of the angle control. With the graphic layer still selected, type “e” to display the effects applied to the layer. You’ll notice that the tint affect has been applied. Click the little twirly next to “Tint” and then click the twirly next to “Map White to” to reveal the following expression (you’ll have to expand the expression area to see it all):
if (this_comp.num_layers<3){
this_comp.layer(“Null 1”).effect(“start_color”).param(“Color”)
}
else{
this_comp.layer(“Null 1”).effect(“start_color”).param(“Color”)+
(this_comp.layer(“Null 1”).effect(“end_color”).param(“Color”)-this_comp.layer(“Null 1”).effect(“start_color”).param(“Color”))*
((index-1)/(this_comp.num_layers – 2))
}
This expression first checks to see if there is only one graphics layer, in which case it just uses the color from the “start_color” control (this is necessary because without this code, the expression will abort on a “divide by zero” error when you first apply it to a single layer). The rest of the expression covers the usual case where there are multiple graphic layers. In that case, the expression spreads the color out so that the top layer gets the “start_color” and the bottom layer gets the “end_color” and the other layers get something in between. Note that the color values are actually 4-dimensional arrays in the form [red,green,blue,alpha] where the value of each element ranges from 0 (black) to 1 (white). We’ll use this info later (fortunately, AE 5.5 introduced user-friendly vector math so we can use normal arithmetic operators on these values and don’t have to worry about them being arrays for right now).
Go ahead and scrub the angle control and watch the expressions do their stuff. Try changing the colors. Getting any creative ideas yet?
automated controls

Select the tab for the “automated controls” comp. This comp is kind of a combination of the first two that we looked at, with slider, angle, and color controls applied to the null layer (select “Null 1” and type Ctrl+Shift+t (PC) or Cmd+Shift+t (Mac) to see them). What’s different about this comp is that we’ve added expressions to the expression controls. With “Null 1” selected, type “e” to reveal the expression controls. Click the twirly next to “Slider Control” and then click the one next to “Slider”. You’ll see this expression:
Math.sin(time*2)*25
This expression just sets up an oscillating value for the slider control. The “25” sets the peak values to +25 and –25. The “time*2” means that the cycle will repeat about every 3 seconds (for the curious: the Math.sin function expects its parameter to be in radians – the cycle repeats every 2*pi radians which is about 6.28).
Now click the twirly next to “Angle Control” and then click the one next to “Angle”. You’ll see this expression:
Math.sin(time*2)*6
This similar expression will vary the angle control from +6 degrees to –6 degrees every three seconds.
Click the twirly next to “start_color” and then click the one next to “Color” to reveal the following expression:
r=Math.cos(time*2)/2+.5;
g=0;
b=-Math.cos(time*2)/2+.5;
[r,g,b,1]
Remember when I mentioned that color is really a 4-dimensional array in the form of [r,g,b,a]? Well this is where we’ll need to know that. This expression varies our start color from red [1,0,0,1] to blue [0,0,1,1] over our 3-second cycle. Notice that green is always off (0) and alpha is always on (1).
Click the twirly next to “end_color” and then click the one next to “Color” to reveal the following expression:
r=Math.cos(time*2)/2+.5;
g=1;
b=-Math.cos(time*2)/2+.5;
[r,g,b,1]
This expression is very similar to the previous one, except that we’ve got green turned on, so we’ll be varying our end color from yellow [1,1,0,1] to cyan [0,1,1,1].
OK. Preview the comp and watch it go.
checkbox

Now we’re going to switch gears a little bit. The checkbox control provides a convenient way to start and stop an animation just by adding keyframes to the checkbox control. Select the tab for the “checkbox” comp. This comp contains a simple “metronome” pre-comp and a null layer with a checkbox control applied. The “metronome” pre-comp contains a simple 1-cycle back-and-forth oscillation of a 25×250 solid that has had the following loop-generating expression applied to the rotation property (see my “Animating a Walk-Cycle” tutorial for more info on this type of expression):
loop_out(“cycle”,0)
In the “checkbox” comp, select the null layer and type “e” to reveal the checkbox control. Click the twirly to reveal the keyframes. The keyframes just toggle the “on” and “off” state of the checkbox control. Select the “metronome” pre-comp and type “rr” to reveal the time remap property. Click the twirly to reveal the expression that has been applied to the time remap property:
if (this_comp.layer(“Null 1”).effect(“Checkbox Control”).param(“Checkbox”) > 0){
xtime = time;
while (xtime > 0 && this_comp.layer(“Null 1”).effect(“Checkbox Control”).param(“Checkbox”).value_at_time(xtime) > 0){
xtime = xtime – this_comp.frame_duration;
}
time – xtime;
}
else{
0
}
Basically, what this expression does is run the metronome animation (starting with the first frame) whenever the checkbox is “on”. Whenever the checkbox is “off” the animation is held at the first frame. Preview the comp and notice the effect. The expression works by looping back in time until it finds the last time the checkbox control was off, figuring out how long ago that was, and plugging that value into the time remapping property of the metronome comp. You might want to play around with moving the keyframes around or adding new ones.
What if you wanted it to operate such that the animation would run while the checkbox was on, hold at the current position while the checkbox is off and resume from where it left off when the checkbox comes on again? Replacing the time remapping expression with this one will do the trick:
xtime=0;
total_on_time=0;
while (xtime<time){
if(this_comp.layer(“Null 1”).effect(“Checkbox Control”).param(“Checkbox”).value_at_time(xtime) > 0){
total_on_time = total_on_time + this_comp.frame_duration;
}
xtime = xtime + this_comp.frame_duration;
}
total_on_time
This expression just loops through the comp frame-by-frame, accumulating “on” time as it goes until it gets to the current comp time. The total “on” time gets plugged into the time remap property. Play around with it if you’d like.
global variables

Sometimes when you’re developing an expression you encounter the situation where it would be really helpful to have access to global variables that you could use to share info between layers. It turns out that in some special cases, you can use expression controls to accomplish this. Select the tab for the “global variables” comp. If you’ve looked at my “Generating Random Motion” tutorial, this comp will look very familiar to you. If not, and you’re interested in how the random part works, check out the other tutorial because I’m only going to cover it briefly here. This comp consists of our new friend the null layer with a point control applied and a simple 50×50 solid (duplicated a bunch of times) with following random-motion expressions applied to the position, rotation and opacity:
position:
i =this_comp.layer(“Null 1”).effect(“Point Control”).param(“Point”)[1]
percent = this_comp.layer(“Null 1”).effect(“Point Control”).param(“Point”)[0];
seed_random(i,true);
target_x = random()*this_comp.width;
target_y = random()*this_comp.height;
seed_random(i-1,true);
old_x = random()*this_comp.width;
old_y = random()*this_comp.height;
[old_x,old_y]+([target_x,target_y] – [old_x,old_y])*percent;
rotation:
i =this_comp.layer(“Null 1”).effect(“Point Control”).param(“Point”)[1];
percent = this_comp.layer(“Null 1”).effect(“Point Control”).param(“Point”)[0];
seed_random(i,true);
target_rotation = random()*720;
seed_random(i-1,true);
old_rotation = random()*720;
old_rotation + (target_rotation – old_rotation)*percent;
opacity:
i =this_comp.layer(“Null 1”).effect(“Point Control”).param(“Point”)[1];
percent = this_comp.layer(“Null 1”).effect(“Point Control”).param(“Point”)[0];
seed_random(i,true);
target_opacity = random()*100;
seed_random(i-1,true);
old_opacity = random()*100;
old_opacity + (target_opacity – old_opacity)*percent;
Preview the comp and you’ll notice it behaves differently from similar comps in the “Generating Random Motion” tutorial (where the motion is somewhat chaotic). In this new comp, the motion is random but synchronized. The key to providing this “master control” is the use of the point control to pass two key “global variables” to the other layers. I used a point control for this because it is a 2-dimensional array and I needed to pass two values. I could have used two slider controls but this seemed easier. If you compare the above expressions to those in the other tutorial you’ll see that all the code that is used to figure out which segment we’re in (“i”) and how far into that segment we are (“percent”) is missing and has been replaced with references to the two values of the point control applied to the null layer. Select the null and type “e” to reveal the point control. Click the twirly and then click the one next to “Point Control” to reveal this expression:
start = 0; //initial conditions
end = 0;
i =1;
tmin = .5; //minimum cycle time (can’t be zero)
tmax = 2; //maximum cycle time
while (time >= end){
i = i+1; seed_random(i,true);
start = end;
end = end + random(tmin,tmax);
}
percent = (time – start)/(end – start);
[percent,i]
This is where all the missing code has gone. The expression in the null layer does all the timing calculations and passes the “i” and “percent” variables to the other layers through the 2 dimensional value of the point control. In the other tutorial, each layer did its own timing calculations so the random numbers generated were different for each layer. In this version, all layers get the timing calculated by the null layer.
[intermission]
OK – that’s it for the basic “how do expression controls work?” part of the tutorial. We’ve looked at all the expression controls except the layer control. I’m still trying to think of a good use for that one. The problem with the layer control is that you can’t keyframe it. If you could keyframe it, it would be a better choice than the slider control that we’ll be using from here on out.
On with the show. Now we’re going to have some real fun applying this global variable business and end up building the world’s greatest cameraman!
we need a plan!
Here’s the basic idea: We want to end up with a comp where we have a bunch of layers flying around at random in 3D space. We want to be able to select a layer at any given time and have our “cameraman” pan, tilt, focus, and zoom in on our subject so that it stays centered in the frame until we decide to look at another layer. So what do we need to do to accomplish this? We already have the “flying around at random” code from the other tutorial, but we’ll have to adapt it to 3D space. We’ll need to develop camera expressions for pan, tilt, focus and zoom. We need a way to tell all our camera expressions which layer we want to focus on . Since all of our expressions need this information, we’ll use a slider control as a global variable. Then, hopefully, all we’ll have to do is keyframe the slider control with the index of whichever layer we want to focus on at any given time.
pulling focus

Select the tab for the “pull focus” comp. In this comp we’ll take the first steps in establishing the concepts that we’ll need to build our amazing cameraman. Notice that we have four layers containing graphics, a null layer with a slider control, and a camera. In this comp the graphics layers don’t move, but they are spread out in “Z” space. Go ahead and preview the comp. You’ll notice that the camera focuses on the graphics one-by-one. Select the null and type “e” to reveal the slider control. Click the twirly and you’ll be able to see the keyframes. If you right-click (PC) or Control-click (Mac) on the keyframes, you see that they just contain the layer numbers in sequence (1,2,3,4). The idea is that when a new keyframe is encountered, the focus will move smoothly from layer it was focused on to the layer represented by the new keyframe over a pre-defined amount of time. Probably the most important element of this comp is the expression applied to the “Focus Distance” parameter of the camera. I need to warn you – this expression is a pretty scary looking monster. It’s really not as bad as it looks, but the code is cumbersome enough that I’m not going to reproduce it here – if you want to see it you’ll need to look at it in the comp. Select the camera layer and click its twirly. Then click the twirly next to “Options” and finally, click the twirly next to “Focal Distance” to reveal the expression:
OK, before you panic, let me just say that the majority of the code is there just to handle special “boundary” conditions that would normally not be the true. For example the situations where the slider control has no keyframes or only 1 keyframe are handled as special cases, as are the cases when the current comp time is before the first keyframe or after the last keyframe. Usually, the current comp time will fall between two keyframes and that’s the case we’ll examine more closely. Also, the code looks more cumbersome due to the lengthy strings it takes to access the keyframes in the slider control. JavaScript has a “with” command that could potentially be used to clean this code up a lot, but it introduces its own level of complexity and issues so we won’t get into that here.
OK. Let’s look at the code. The first line defines a variable called “focus_time” which is the transition time (in seconds) that we want it to take to change focus from one layer to the next. Let’s skip over the “boundary” condition code I was talking about earlier and get down to the good stuff. Look at the section that starts with “i=n;” (it’s about 2/3 of the way down the code). Basically this part of the code is a loop that cycles backwards in time from the last keyframe until it finds the keyframes on each side of the current time. Then it looks to see if it is within the transition time where it should be changing focus. If it is in the transition area, it calculates what percentage of the transition should be complete now. Otherwise it just focuses on the new layer. Note that this code is a little different than the code you’ll see in the final comp because in this comp the camera is not panning or tilting so the expression is just calculating the Z distance to the graphic layers.
Now I know what some of you are thinking at this point. Wouldn’t it just be a lot easier to keyframe the focus distance directly and eliminate all this other stuff? Patience, Grasshopper.
focus and zoom

Select the tab for the “focus & zoom” comp. This comp is virtually identical to the previous comp, except that an expression has been added to the camera’s zoom property which sets it equal to the focus distance. Preview the comp to see the effect. You can examine the expressions if you’d like, or mess around with the keyframes. It’s pretty much what you’d expect by this point.
pan, tilt, focus, and zoom

We’ve finally arrived at the grand finale! You’re going to need the Production Bundle to get the full effect of this (because of the Fractal Noise filter used for the background). If you don’t have the production bundle, I suspect it will still mostly work – you should just see everything against a non-moving plain background. Select the “pan, tilt, focus & zoom” tab. Go ahead and preview the comp now and we’ll go through it when you’re through.
There are several things going on here. We’ve got our graphic layers with the random motion expression pumped up to do 3D. Feel free to look at the code if you want to see how to do that and compare it to the code in the 2D version in the “Generating Random Motion” tutorial. The camera has an expression very similar to the focus expression (seen above) applied to the camera’s “point of interest” property to keep the camera pointed at the flying objects. The difference is that the “point of interest” calculation is equal to the position of the layer that the camera is focusing on (except during the transitions, when the “point of interest” is some percentage along the path between the two layers. Since the expression for the “point of interest” is doing all the work, the expressions for the focus distance and zoom properties simply become:
length(position,point_of_interest)
This is just the distance from the camera to the point of interest. I need to say a few words about the background for this comp (the “clouds” layer). I think it’s the background that really sells the effect of the panning and tilting because without the background to provide a visual point of reference, it would just look like the layers were moving, one-by-one, into position in front of the camera. To avoid having to build an enormous 3D background layer that would fill the comp window no matter which way the camera points, I decided to just use a 2D solid with Fractal Noise applied, because Fractal Noise has an offset parameter that essentially makes it extend infinitely in any direction. The trick is to tie the offset to the “point of interest” so that the background appears to be responding to the camera movement. This expression, applied to the “Offset Turbulence” parameter of Fractal Noise seems to do the trick:
z=this_comp.layer(“Camera 1”).point_of_interest[2]-this_comp.layer(“Camera 1”).position[2];
y=this_comp.layer(“Camera 1”).point_of_interest[1];
x=this_comp.layer(“Camera 1”).point_of_interest[0];
y_angle=radians_to_degrees(Math.atan(y/z));
x_angle=radians_to_degrees(Math.atan(x/z));
[this_comp.width/2-y_angle*12,this_comp.height/2+x_angle*12]
This expression was developed somewhat by trial and error, with a little bit of science thrown in.
Spend some time exploring this comp. Look at the expressions in position property of the graphics layers. The 3D world space for the random motion of these layers is constrained to 1.5 times the comp width for X, 1.5 time the comp height for Y and 1500 for Z. You can edit these values in the expression to see the effect. Fiddle around with the parameters and the expression for the Fractal Noise. Change the pan_time parameter in the camera’s “point of interest” expression. Change the keyframes (or add new ones) in the null’s slider control (remember to stick with the integer values of 1,2,3 and 4, corresponding to the layer indices of the 4 graphic layers). Have fun!
Well, there you have it. Congratulations if you stayed the course. Hopefully you’ve picked up enough information about expression controls and how to combine them with expressions to make yourself really dangerous!
Enjoying this tutorial? 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