How to make a “Float Away Video Wall” in After Effects via scripting

I was watching HBO a bit ago and noticed a fun promo where the playing video breaks into a grid and slowly floats away out of frame (Anybody familiar with the work know where I can find a link to a web video? update: 08-07-2006 Greg Grusby correctly pointed me to this page from Shilo which makes it clear that my script is just a tech demo <grin>.). Although I’m not a fan of duplicating a concept just to mimic it technically, I thought it might be fun to work through the steps necessary to pull off something similar via After Effects scripting.

Update July 12, 2006

Thanks to Jeff Almasol for the tips about Alpha Add and Null names in the comments below. I wasn’t aware of it, but it looks like his script rd_Slicer addressed a lot of this how-to without the float away expression in a really nice package. Jeff’s scripts and resources at redefinery are must see.

The first order of business in our script is to check that all of the conditions necessary to run the script are met. We set up a function checkPrerequisites() to check that one Footage layer is selected. To do that we first grab the project.activeItem and check to make sure it is a Comp item

var activeItem = app.project.activeItem;

if ((activeItem != null) && (activeItem instanceof CompItem)){
	var selectedLayers = activeItem.selectedLayers; 
}else{
	var selectedLayers ='';
}

If it is, we iterate through the layers and make sure a single Footage item is selected.

for (var i = 0; i < selectedLayers.length; i++)
		{
    		layer = selectedLayers[i];
 
    		if (layer.source instanceof FootageItem){
        		matchingLayers[matchingLayers.length] = layer;
			}
		}

We then check that the matchingLayers array only contains a single item and alert the user if that is not the case. We also add a reference to our layer so that we can use it later in our script.

if(matchingLayers.length > 1 || matchingLayers.length == 0){
				
				selectedItemInfo.source = '';
				selectedItemInfo.width = '';
				selectedItemInfo.height = '';

				alert(selectAlert);	
			}else{
				
				selectedItemInfo.source = matchingLayers[0];
				selectedItemInfo.width = matchingLayers[0].width;
				selectedItemInfo.height = matchingLayers[0].height;
				 
				if(w != null){
					w.show();
				}else{
					alert("Couldn't build UI");
				}
				
			}

Next we need to set up a UI for the script. We know we need to prompt the user for both the height and width of the grid. We also want to get some timing information about the length of the effect. This UI code...

 function buildUI(){
		
 if (win != null) {
	 win.GridPnl = win.add('panel', [20,25,230,145], 'Video Grid Size:');
		 
	 win.videoGridWidthText = win.GridPnl.add('statictext', [15,20,105,30], 'Width:');
	 win.videoGridWidthValuesText = win.GridPnl.add('statictext', [15,35,205,45], '2                                                       10');
		
	 win.videoGridWidth = win.GridPnl.add ("slider",[10,48,205,58], 5, 2, 10);
	 win.videoGridWidth.value = 5;
		
	 win.videoGridHeightText = win.GridPnl.add('statictext', [15,70,105,80], 'Height:');
		 
       win.videoGridHeightValuesText = win.GridPnl.add('statictext', [15,85,205,95], '2                                                       10');
	 win.videoGridHeight = win.GridPnl.add ("slider",[10,98,205,108], 5, 2, 10);
	 win.videoGridHeight.value = 5;
		
	 win.SpeedPnl = win.add('panel', [20,150,230,195], 'Speed:');
	 win.speed = win.SpeedPnl.add ("slider",[10,25,205,35], 100, 20, 300);
	 win.speed.value = 100;
		
	 win.helpBtn = win.add('button', [20,205,50,225], '?', {name:'help'});
     	 win.helpBtn.onClick = usage;
     	 win.cancBtn = win.add('button', [105,205,165,225], 'Cancel', {name:'cancel'});
     	 win.cancBtn.onClick = function () {this.parent.close(0)};
     	 win.okBtn = win.add('button', [172,205,230,225], 'OK', {name:'ok'});
      win.okBtn.onClick = function () {main();};
      	
		 
      }
   return win

}

produces a UI that looks something like this...

Float Away Video Wall After Effects Script UI

So, let's break down the meat of the script.The first thing we need to think about are the individual grid elements. First, we grab information from our UI and set up units. We use our selectedItem object that we we created earlier to calculate and set the width and set up local variables for the grid height and width as well as the speed of the transition.

     //calculate units
   	var footageWidth = selectedItemInfo.width;
     var gridWidth = win.videoGridWidth.value;
     var widthUnit = footageWidth / gridWidth;
     var footageHeight = selectedItemInfo.height;
     var gridHeight = win.videoGridHeight.value;
     var heightUnit = footageHeight / gridHeight;
     var speedBase = win.speed.value;

We then loop through the grid width and grid height and duplicate our selected layer. For each duplicated layer we set a mask based on its location within the grid and set an expression which will ease between the grid location and a null which becomes the template for our destination.

//set an UndoGroup so we can back out of the script if we get unexpected results
 app.beginUndoGroup("Apply Float Away Video Wall");
     //loop through grid height and grid width

     for(x = 0; x < gridWidth; x++){
     	for(y= 0; y < gridHeight; y++){
     		
           //duplicate our selected layer 
           var itemToMask = selectedItemInfo.source.duplicate();
     	     
           //create a mask for our new layer	
     		newMask = itemToMask.Masks.addProperty("Mask");
		newMask.inverted = false;
		myMaskShape = newMask.property("maskShape");
		myShape = myMaskShape.value;
		myShape.closed = true;
		
           //create the vertices for our mask based on grid units
		var tempVertices = new Array();
			
		tempVertices.push([x * widthUnit , y * heightUnit]);
		tempVertices.push([x * widthUnit + widthUnit , y * heightUnit]);
		tempVertices.push([x * widthUnit + widthUnit , y * heightUnit + heightUnit]);
		tempVertices.push([x * widthUnit , y * heightUnit + heightUnit]);
		myShape.vertices = tempVertices;
		myMaskShape.setValue(myShape);
		
           //set the length of the transition for this grid element randomly within our transition speed
		var randomTime = Math.random() * speedBase;
		
          //make expressions for our grid item
          //we set two expressions, one for position and one for orientation the expressions first set a time based on our random time above
 	    //and then ease from our first position to a location of a template null

         var positionExpression = 't = timeToFrames(t = time + thisComp.displayStartTime, fps = 1.0 / thisComp.frameDuration, isDuration = false) /' + randomTime + '; position =ease(t, position,  thisComp.layer("Null 1").position);';
     		var orientationExpression = 't = timeToFrames(t = time + thisComp.displayStartTime, fps = 1.0 / thisComp.frameDuration, isDuration = false) /' + randomTime  + '; orientation = ease(t, orientation, thisComp.layer("Null 1").orientation);';
     		
     		itemToMask.position.expression = positionExpression;
     		itemToMask.orientation.expression = orientationExpression;
     		
     	}
     }
     
     app.endUndoGroup();	

That pretty much wraps up the script. The application of the script is pretty straight forward. Download and move the script to the After Effects:Scripts folder. Select a single footage layer and run the script with your settings adjusted accordingly. The layer is duplicated with the appropriate settings and the expressions are applied. To adjust the destination of the video wall move and adjust the Null Object position and orientation. NOTE:(Due to a limitation in addNull() via scripting we can not alter the name of the null via scripting, so the script checks for the existence of a single Null and creates one if none is available. Everything should work fine as long as there is a single Null available in the Comp). When I run the script with my example image part of my Comp window looks like this with duplicated layers and an expression applied to each layer...

Comp Window in After Effects after script is applied

You might argue that you could pull off something similar with built-in plugins like card dance without the overhead of duplicating layers. I think that might be so in certain circumstances, but the flexibility of scripting allows you to experiment with timing, grid spacing and other attributes. What would happen if we staggered our timings in the order we created the grid? What if we increased the size of the grid elements as we moved inward? Your really only limited by your ability to actualize your ideas via scripting.

Source JSX script

Downloadable Zip Version including Project and Sample files

9 Comments

  1. Hey Dale,

    Nice script. You might consider using Alpha Add blending mode on the layers to remove the hairline gap that can sometimes appear along their edges. (Thanks to the Meyers’ for that tip.) I provided it as an option (default=checked) in my rd: Slicer script.

    Also, although addNull() can’t accept a name arg, you can rename it afterwards by setting its ‘name’ attribute. Is that what you were referring to?

    Jeff

  2. Dale said

    Hey Jeff,

    Thanks for the heads up on the Alpha Add, I had noticed the hairlines occasionally and it’ll be great to have a fix.

    Does the .name attribute for a null have an intermediate property? destinationNull.name = ‘destination’; didn’t seem to work for me. I’ll play with it again, it might have just been a typo that I didn’t follow through thoroughly and when it didn’t have a name attribute in the constructor I thought it was unavailable.

    Thanks again,

    Dale

  3. Dale,

    Are you getting an error when setting the layer object’s name attribute? It works here for me in 6.5 and 7.0. Note that setting the layer’s name doesn’t switch the column from Source Name to Layer Name.

    Jeff

  4. Dale said

    Jeff,

    … Note that setting the layer’s name doesn’t switch the column from Source Name to Layer Name.

    ahh yes, there would be the embarrassing user error. I of course didn’t change the column to Layer Name when I checked the name change. I’ll rework the Null logic and add the alpha add blend mode in the next version of the script as well as add a correction to the body of the post.

    Thanks for all your help Jeff…you’re the best.

    To anybody interested I’ll try to add them this weekend, I’m a bit in the weeds right now.

    Best,

    Dale

  5. […] A common scripting requirement is to pull random values from within a defined range. For example, in the block dissolve transition the individual blocks of video are extracted from the video in a random order. As an example, let’s revisit our float away video wall script and customize it to work in the same manner as a block dissolve but with a little more flair. We will scale the individual blocks up instead of just transitioning on and off. […]

  6. Greg Grusby said

    Hi Dale,

    Wondering if this HBO piece might be the one you’re referring to:

    http://www.shilodesign.com/index.php?section=entertainment&id=4

    It was created to promote HBO Documentary Films and seems to reflect the one you decribed.

    Cheers,

    Greg (TA)

  7. Dale said

    Hey Greg,

    That’s the one! thanks for the pointer. I’ve updated the post…

    Thanks,

    Dale

  8. Greg Grusby said

    Just a brief note,

    The studio that created the HBO Docs piece was Shilo (not ‘Shiloh’ as posted in the update.)

    Cheers,

    Greg

  9. Dale said

    >The studio that created the HBO Docs piece was Shilo (not ‘Shiloh’ as posted >in the update.)

    Doh!

RSS feed for comments on this post

Comments are closed.