Creating 3D Ribbon Text
Creating 3D Ribbon Text
CreativeCOW Adobe After Effects Tutorial

Creating 3D Ribbon Text
David Langley David Langley
Durham, New Hampshire

©2006 David Langley and All rights reserved.

Article Focus:
In this tutorial Dave Langley demonstrates how to create a 3D ribbon of text that flows smoothly around curves up and down slopes moving around and interacting with other elements using only core tools in After Effects.

Download Movie Project file: .zip


The Goal

Create ribbons of text flowing through 3D space and around objects in a scene,


The Objective


To build a relatively easy to edit yet fairly sophisticated 3D text animation with only core tools in After Effects; using expression controls to manipulate expressions, effects, and transforms applied to multiple layers simultaneously across multiple comps, collapse transformations and use nested comps and time remapping to accomplish the goal. Ok, a long, possibly scary statement for some, but don’t let that stop you.


Background Information

The very powerful text animation tools in After Effects are great for animating text along a path in 2D space. You can make a text layer 3D, but it is still a flat plane. The text animation tool set doesn’t directly venture into 3D where each character retains its relationship with a text string but also maintains an independent relationship on a 3D motion path in 3D space.

 So, that’s it, we’re done, It’s time to jump over to a 3D App? Not by a long shot.

Joe Chao created an excellent tutorial with streaming text in 3D space. His text layers stream nicely spiraling around a pillar with a lot of variation. If you haven’t seen his tutorial you should definitely check it out in the tutorials section here at the Cow. Yet each text string is still a flat plane because all characters reside on the same layer.

Ok, this has to be as far as it goes, it must be time to jump over to a 3D App, isn’t it? Not yet.

I want to explore how one might create a ribbon of text that orients along a path in 3D space with each character flowing smoothly around curves and up and down slopes, all part of one text string but each character residing independently in 3D space. The text "ribbon" also would have the ability to flow around and interact with other objects in the scene.

This definitely can’t be done in After Effects; surely you must stop now and jump on that 3D App, right? Wrong.

As it turns out by combining the power of text animation features with layer transformations and a few expressions it is possible to create such a "ribbon" of text.  Although the characters themselves will not have depth they will flow smoothly through 3D space and around and interact with other objects in the scene. 

Before we dive in, I would like to thank Dan Ebberts, Mylenium, Joe Chao and the other authors of various expressions based tutorials found here at the Cow. Dan, Mylenium, Joe, et al if you happen to see this, thanks for inspiring me to dust off my math and code writing skills and explore the power of expressions.


Getting Started

I used only the Pro bundle of AE 6.5 (yes, I plan to upgrade soon) and I assume you have a pretty good handle on using After Effects. I know for some, when someone mentions the "E" word (expressions) a dark cloud forms in your brain. However, I think you will find this tutorial won’t be too bad, and I will endeavor to describe what is going on with the expressions as we go. So, if you are well versed in expressions forgive me and if you are not I hope the explanations will be of assistance. All the elements you need including a completed ribbon text element are in the project files so you can dissect it. Therefore, I will focus efforts strictly on the creation of the ribbon text element.

A quick tip: When working with expressions I have found it useful to copy and paste wherever possible to save time and help prevent errors. Also, a lot can be done with the Pickwhip, so use it whenever you can, even just to grab syntax while composing your expressions.

Ok, let’s grab our horn-rimmed glasses, pop in our pocket protectors and get on with it. Oh, and if you have an old lab coat lying around, put it on.

Figure 1: Viewing angle showing how the ribbon text interacts with layers in 3D space.

Note: The keyed footage is from Mini DV and has been compressed for download, so please ignore any quality issues

Nulls - Invisible Workhorses

Create a new comp and name it "Guide_A" you can set whatever size and duration you want; mine was set to 720 x 480 with duration of 10 seconds. Add two Null Layers; name the first one "Controls" and the second one "Txt_Guide".

We are going to use an alphanumeric system as a comp naming convention. We need only include the alpha portion i.e. "_A" at the end of comp names and when we duplicate comps in the project window the numeric portion will be added automatically by After Effects.

To the Controls null add two Slider Controls you will find them in the Effects and Presets palette in the category labeled Expression Controls or simply type slider in the Contains: text box of the Effects and Presets palette. Name the first slider  "Txt_Tracking" and the second one "Comp_Spacing". Right click on the Txt_Tracking slider (the actual slider not the effect name) and choose edit value, then edit the range to go from 0 to 5 and set the value to 1.0 Right click on the Comp_Spacing slider choose edit value and edit the range to go from -15 to 15 and leave the value at 0.0. We will use these controls later to adjust the space between individual text characters and spacing between multiple text comps.

This null is simply providing an interface for controlling aspects of the animation therefore; it will not need to be animated. You will need to turn on its 3D layer switch so when we nest compositions and collapse transformations our 3D properties function correctly.

The reason we are using a separate controls null is so we can throw a lot of controls in here and not worry about inadvertently screwing up our Txt_Guide null. I want the text guide to do only one thing, provide a motion path for our text.

Let’s add some more controls. Add three more slider controls and one color effect control. Name the first new slider "X_Roll_Rate" the second new slider "Anchor_Pt", the third new slider "Scale" and name the color control (your guessed it)"Color". You could add any number of controls but we will limit our discussion to these few for now. Right click the X_Roll_Rate slider, leave the range at 0-100 and just change the initial value to 100. Right click the Anchor_Pt slider and set the range from -100 to 100, leave the initial value at 0. Right click the Scale slider and set it to range from 0-500 and set the value to 100.Click the color picker on the Color control and set Hue to 200, Saturation to 20 and Brightness to 100


Figure 2: Expression Controls showing how they affect tracking and spacing on a completed ribbon text element.


To the Txt_Guide null make the following adjustments:

  • Make the layer 3D by clicking the 3D check box.
  • Anchor Point: 50, 50 (not required but it can aid in seeing the movement along the path)
  • Position: Set a keyframe at time 0 with a value of 720, 240, 0 or whatever the width of your comp is, and set a keyframe at the out point with the values 0, 240, 0. This will give us a straight right to left motion path to help set up our ribbon of text. We will create a final motion path for the animation later.


Adding Text to Play With

Create a new comp with the same size and duration as the first one and name it "Text_A".

Add a null layer and name it "Global_Tracking". To this null add a Slider Effect Control and name it "Tracking". Right click on the slider and edit the value to range from 0-5. Alt(Opt) click the stopwatch icon for the slider and add this expression:




What we are doing here is setting up a way to globally offset the individual characters of our text within the Text_A comp from the Controls null in the Guide_A comp. You might be thinking it seems redundant to have a slider controlling a slider, but bear with me, when we duplicate our layers and comps multiple times we will want to make changes on only one or two layers and have those changes "filter down" through each layer and across comps without having to manually change every layer. This null with its slider control will allow us to do just that. Again, there is no need to animate this null, but as before, you will need to turn on its 3D layer switch.


Setting the Stage

Now create a text layer make sure it is below the Global_Tracking null layer in the timeline. Type anything you want, I used the word TEXT. We will change it to the final text later. In the paragraph palette set the text to align left and in the character palette select Arial Bold as the font, and set size to 30px. In the timeline do the following:

  • Make the layer 3D by clicking the 3D check box.
  • Ctrl(Cmnd) + Alt(Opt) + O and set Auto Orient to Orient Along Path:
  • Twirl down the layer properties and make the following adjustments:
  • Text: Add an animator for opacity and name it "Opacity".
  • Set the animator opacity property to 0%; the text should disappear for now.
  • Twirl down the Animator Group, then
  • Twirl down the Range Selector, then
  • Twirl down Advanced, and set the following parameters
    • Units to Index,
    • Based On to Characters Excluding Spaces,
    • Mode to Subtract.
  • Back under Range Selector
    • Leave Start at 0.0
    • Set End to 1.0
    • To Offset: add the following expression:

The first character of your text should reappear, and the remaining characters should remain hidden. What we are essentially doing is masking characters. What the expression does is take the layer number and subtract 2 from it, which, in this case will be 0 thus leaving the Start and End parameters unchanged and as a result only the first text character is revealed. Simple enough, but it has real power when we duplicate this layer, because it will cause the Start and End parameters to increment by one on each duplicate of the layer; e.g. layer number 3 – 2 = offset of 1; the result in this case: Start = 1 and End = 2 which reveals the second character only.

Now let’s add another animator, this time for Anchor Point. Name the animator "Characteristics"; to the right of the animator name click to add a Scale property and Fill Color: RGB property to this same animator group. Now add the following expressions or use the Pickwhip to select the appropriate slider from the expression controls:

  • To Anchor Point:

  • To Scale:
    Scale= comp("Guide_A").layer("Controls").effect("Scale")("Slider");
    [Scale, Scale]

  • To Color:

This will allow us to globally change the anchor point, scale and/or color of our text from the Controls null in the Guide_A comp.

Anchor Point: adjust the x and y values to center the anchor point on the first character.

Position: add the following expression:


This expression links this layer’s position with the position of the null named Txt_Guide in the comp named Guide_A, which we created earlier. So, wherever that null goes this layer will go.

Orientation: add the following expression:

a= Math.atan(position[0]);


  b=  Math.atan2(position[0],position[1])


  b= Math.atan(position[1])



This expression tells this layer to assume an orientation that is parallel to the motion path. The if-else statement helps to mitigate an issue described in the Other Considerations section at the end of this tutorial.

X Rotation: add the following expression:


This expression links this layers X Rotation to the null named Txt_Guide in the comp named Guide_A. So, whatever X angle that null has this layer also has.


Let’s add our final text:

  • Under Text: Source Text: Alt(Opt) click the stopwatch and enter your final text string, enclosed in quotes, in the expression field; this is one of the strings I used:
    • "3D RIBBON TEXT"
  • Material Options:
    • Set Casts Shadows to On
    • Light Transmission to 25%
  • Motion Blur: You might consider checking the Motion Blur check box. (CAUTION: Motion blur significantly increases render times so make sure the "M" switch is not selected or RAM previews will be slow, or may not even cover the entire animation.)

Figure 3: Expression applied to Global_Tracking null and the first text layer.


A Little Duplication

In the Timeline Window duplicate (Ctrl(Cmnd) + D )the text layer (layer 2) you will see the second character revealed, now make the following changes:

  • Add a Slider Effect control and name it "Initial_Tracking" right click the slider and edit the range to go from 0-10 and set the initial value to 0.25

  • Text: Source Text: Replace the text string with the following expression:

    This links the text on this layer to the previous layer, allowing us to globally change text by altering the text on only one layer but having that change filter down throughout all text layers.

  • Text: Characteristics: Replace the existing expressions with these:
    • Anchor Point:

    • Scale:

    • Color:
      Again, these are more global change, filter down mechanisms.

  • Anchor Point: adjust the x value only, to center the anchor point on the second character. The character will move over the top of the first character.

  • Position:replace the expression with this one:

    Initial_Tracking= effect("Initial_Tracking")("Slider");


    This expression looks at the two slider effect controls (Initial_Tracking and Global_Tracking) and sets character tracking based on their settings multiplied together and the result is subtracted from the position value of the first character at any point in time. Because we cannot access the width value of individual characters and some characters like "I" are narrower than others like "M" we are using Initial_Tracking to compensate for that difference. Ultimately we are going to use the Txt_Tracking slider in the Controls null in the Guide_A comp for overall character tracking. If this isn’t clear I hope seeing the results will clarify it for you. If it remains unclear consider the immortal words of Dan Ebberts (and I’m paraphrasing): "Just know that it works and file it away"

  • Initial_Tracking: adjust the slider until you are satisfied with the space between the first and second characters. The 0.25 initial value should give you a good starting point, but you will want to adjust it some, I set mine to 0.33.

  • X Rotation: replace the expression with this one:

    L= thisLayer.index;
    Shift= (L-2)/ comp("Guide_A").layer("Controls").effect("X_Roll_Rate")("Slider");


    What we have here is an expression that takes the layer number subtracts 2 from it and divides by a value set with a slider; this gives us a percentage of time which we subtract from the current time. The result rotates this layer by the same amount as the previous layer but trailing it in time. Due to the fact that we set the X_Roll_Rate slider initial value to 100 each new layer will increment the shift by 1/100th of a second. You can alter the rate at which the rotation changes by changing the X_Roll_Rate slider in the Controls null of the Guide_A comp.


Ok, if you are still with me and haven’t passed out; we are now ready to reveal the remaining characters of text by duplicating this layer. So, hit the E key to reveal Initial_Tracking and twirl down the effect then hit Shift + A to reveal the anchor point property. These are the only parameters we will need to make changes to as we reveal our text. Note: You will always be adjusting the bottom most layer in the stack, so when you duplicate a layer you will need to select the bottom most layer and make your changes. Also we are centering the anchor point before adjusting initial tracking.

Figure 4: Expressions applied to the second text layer.


Guides and Calibrated Eyeballs

It may be helpful to set some guides to adjust the tracking between characters and word breaks. Move to about 2 seconds in the time line and adjust the comp window so you can see the edge of the comp. In the Comp Window set two guides, one at the right edge of the first text character and one at the left edge of the second text character. These will help with consistent character alignment by moving the Current Time Indicator as needed to set up Initial_Tracking for each character as it is revealed. When you get to the first space between words you can add one more guide to help set consistent spacing for word breaks.  I found that at a font size of 30px I wanted about a 7 pixel space between characters, and around a 25 pixel space between words.

Use the up and down arrow keys to move the characters into alignment, use Shift + up or down arrow keys to move the characters in increments of 10, use the Ctrl(Cmnd) + up or down arrow keys to move 1/10 increments. When adjusting the anchor point you may find it helpful to temporarily turn off visibility for the previous character. Also use the Page Up or Page Down Keys to move the Current Time Indicator; adding Shift + Page Up or Page Down moves it in increments of 10.

Note: I don’t know if this is a program bug or not, but when you subtract a value with an even 0.5 increment the character will flip around on the Y axis unexpectedly as the text moves along the motion path, strange, so adjust accordingly e.g. use 0.49 or 0.51.


Revealing Characters

Select the second text layer (layer 3) and duplicate it to reveal the third character. Now make the following changes:

  • Anchor Point: adjust the x value only, to center the anchor point on the third character. The character will move over the top of the second character.

  • Effect: Initial_Tracking: Adjust the Initial_Tracking slider until you are satisfied with the space between the second and third characters. Move the Current Time Indicator and use the guides we set up in the comp window, but remember they are just guides, so don’t be afraid to resort to using a calibrated eyeball.

From here you continue to duplicate layers, select the bottom most layer, center the anchor points on the newly revealed character, and adjust the Initial_Tracking slider, until you have revealed and adjusted all the characters of your text. It is important to get this Initial tracking of each character exactly as you want it now. If you don’t when you duplicate the text comp several times you will have to manually adjust many more layers or delete comps and text layers and start again.


Duplicating Comps

In the Project Window select the Text_A Comp and duplicate it.

Double click the new Text_A 2 Comp and make the following changes to the first text layer (layer 2) only:

  • Source Text: replace the character string with the following expression:


  • Position: replace the expression with this one:

    Control= comp("Guide_A").layer("Controls").effect("Comp_Spacing")("Slider");


  • X Rotation: replace the expression with this one:

    L_Num= comp("Text_A").numLayers;
    Shift= (L_Num)/ comp("Guide_A").layer("Controls").effect("X_Roll_Rate")("Slider");


    These expressions are more global change mechanisms.


Figure 5: Expressions applied to the first duplicate text comp.


Now in the Project Window select the Text_A 2 Comp and duplicate it as many times as you require, fewer for long text strings and more for short text strings. Double click Text_A 3 Comp and make the following changes again only to the first text layer (layer 2):


  • Position: make the changes highlighted in red only:

    Control= comp("Guide_A").layer("Controls").effect("Comp_Spacing")("Slider");


  • X Rotation: make the changes highlighted in red only: 

    L_Num= comp("Text_A").numLayers;
    Shift= (L_Num)/ comp("Guide_A").layer("Controls").effect("X_Roll_Rate")("Slider");


Figure 6 Altered expressions in the second duplicate text comp.


Double click Text_A 4 Comp and make the following changes once again only to the first text layer (layer 2):

  • Position: make the changes highlighted in red only:

    Control= comp("Guide_A").layer("Controls").effect("Comp_Spacing")("Slider");
  • X Rotation: make the changes highlighted in red only:

    L_Num= comp("Text_A").numLayers;
    Shift= (L_Num)/ comp("Guide_A").layer("Controls").effect("X_Roll_Rate")("Slider");



Continue to make these same changes on each duplicate text comp that you may have. Note that all we are doing is changing the multiplication factor (highlighted in red above) in the Position and X Rotation expressions to be one less than the comp number of the comp we are changing. This tells each Text_x comp to follow the Txt_Guide but position itself the proper distance away from and delay its X Rotation to trail behind the Text_x comp in front of it.

Completing a Ribbon Text Element

Drag and drop all the Text_A n comps into the Guide_A comp and Collapse Transformations on each text comp. The ribbons will be on top of one another. Turn off visibility for all but the first text comp this will speed up RAM previews. Now create any motion path you like by keyframing the Position property of the Txt_Guide layer and adjusting the Txt_Tracking control to suit your situation. Preview the animation and make adjustments to the motion path. Now turn on visibility for all text comps and adjust the Comp_Spacing slider control to the desired spacing between iterations of your text string.

To eliminate accordion like opening and closing of tracking and spacing in the text and maintain a constant rate of speed along the motion path turn on Roving Keyframes for all keyframes between the first and last one. Do this by twirling down the position property in the timeline and clearing the checkbox under the keyframes.  Your last position keyframe will likely not be at the out point of the timeline but somewhere before it. This allows the entire ribbon to complete its movement along the motion path. You can even create a motion path with expressions and no keyframes.


Figure 7: You must collapse transformations to pass through 3D position attributes.


Figure 8: The ribbon element and its motion path.


Building A Final Multi-Ribbon Animation

Take all the Guide_x comps and place them into a new comp named "Ribbon_Final"; collapse transforms, add cameras, lights and other elements etc. To make the ribbon flow around an object e.g. an actor shot against a green screen or isolated by rotoscoping, just turn on its 3D switch and move it to wherever you want it in z space. Then open the appropriate Guide_x comp and create the motion path as describe above.

Now you can apply time remapping the Guide_x layers to speed up, slow down, stop or even reverse the motion of your ribbons. Time remapping allows tremendous flexibility in the final animation.

To use time remapping click on a Guide_x layer in the Ribbon_Final Comp, then in the menu bar select Layer: Enable Time Remapping. This adds the time remapping property to the layer along with 2 keyframes; one at the in point and one at the out point. Now you can add whatever number of keyframes you require; twirl down the graph and adjust it to play with time. Flattening out the graph line between two keyframes causes the movement to stop, changing the slope of the line will increase or decrease speed and/or reverse direction. For more information on time remapping consult the AE help files.


Changing Your Mind

You can change your text by entering a new text string in the expression field of the first text layer of a first iteration Text_x comp; then check each layer and center any anchor points that are out of place and adjust the Initial_Tracking sliders. Remember to center the anchor point before changing Initial_Tracking. It may require that you follow the procedure for duplicating text layers to reveal new characters which exceed the total number of characters in the prior text string.

Changing fonts and switching between upper and lower case converts reasonably well without having to make a lot of adjustments. Except possibly where the character widths differ greatly from the previous character that occupied that spot, and especially where word breaks don’t line up with the previous word breaks. So, check and center anchor points and adjust Initial_Tracking sliders on individual text layers as needed.

You will be able to change the color, scale and anchor point of any ribbon element by using the effect controls we added to the Guide_x comps. You can disable the expressions on the first text layer of any Text_x comp and change these characteristics for just that one text comp. To change the font you will need to select all text layers in a Text_x comp and make the change in the Character Palette.


Go Nuts

The possibilities, if not endless, are at least extensive, for example, if you like the flow of a particular ribbon, but you want another copy to ride right above it; you can duplicate the first iteration of the Text_x comp double click the new copy to open it in the timeline, twirl down the first text layer’s properties and under the Text property Characteristics animator group, disable the expression for the anchor point and adjust the y value to move the new ribbon above the old version.

You can combine the wiggle expression with position keyframes in the Txt_Guide layer in the Guide_x comp, which will cause the ribbon to ripple. It can range from mild to wild depending on the settings; try wiggle(1,25) preview the animation and we get a nice waving ripple. Now try wiggle(1,250) you will need to adjust Txt_Tracking and Comp_Spacing for this one. Ok, maybe that was too much, but you get the idea.

As mentioned above you can create a motion path with expressions and no keyframes. Try this expression on the position property of the Txt_Guide layer in any Guide_x comp then preview the animation:

Vert_Change= 50;

radius= 250;

a=  -Math.cos(time) * radius + thisComp.width * 0.5;

b=  -Vert_Change * time + thisComp.height;

c= Math.sin(time) * radius;


Your ribbon should move in a spiral from the bottom of the comp to the top. Don’t forget to adjust Txt_Tracking and Comp_Spacing.


A Note on Adding Effects

You can add effects to your ribbon text such as glow etc. and you can add controls to globally adjust the effects. However, if your ribbon interacts with other elements in 3D space the effects must reside on every text layer in every Text_x comp where you want the effect. Adding effects to nested comps with collapsed transformations won’t allow us to maintain the required 3D relationships, and Adjustment layers won’t do the trick either. So if you require an effect to achieve a specific look i.e. the Glow effect you can add it to the first text layer in a Text_x comp, set up its parameters then link them to sliders or other effect controls via expressions that can reside in the Controls null of Guide_x, then copy the effect, select all the other text layers in the comp and paste the effect. I added a slight glow effect to my ribbon text.


Adding Complexity

To add different ribbons you simply duplicate comps in the Project Window and rename i.e. Guide_A to Guide_B and Text_A  to Text_B … then make changes to the expressions in the tracking layer and the first text layer of the new text comp to refer to the appropriate Guide_x comp i.e. changing  all references from "Guide_A" to "Guide_B". Warning, copying multiple text comps can quickly become confusing as to which goes with which. So, you should copy just the first iteration of a text comp e.g. Text_A , rename e.g. Text_B and then duplicate that comp making the necessary changes to the new duplicate Text_B n comps as delineated above.

Then open Guide_B in the timeline and you will see that the Text_A n comps are still residing in Guide_B. To replace them with the Text_B n comps just delete the Text_A n comps from the timeline window and drag and drop the Text_B n comps into the timeline window. Alternatively, you can highlight all the Text_A n comps in the timeline and then select all the Text_B n comps in the project window and Alt(Opt) drag and drop them on top of the Text_A n comps in the timeline window. Animate the new Guide_B position and x rotation to flow somewhere else.


Other Considerations

Keep in mind that using time in the valueAtTime attribute causes the characters to start out and end up all overlapping one another, so you may want to have your first and last keyframe positions outside the viewing area. Or better, work time remapping into your animation to start the text fully extended and/or stop it still fully extended.

Also, position values in the negative range can cause the text characters to flip unexpectedly. I was able to mitigate this issue with the orientation expression if-else statement so that it appears to at least work visually; so long as x and y position values are not both negative at the same time. I cannot consider it a fix, so beware of this and keyframe accordingly. It only really seems to be an issue in deep z space when you want the ribbon to extend beyond the top and left viewing area of the comp.

Mylenium provided the following explanation to a query I made regarding this issue:

"This is a simple math issue known as "gimbal locking" (every 180 degrees all results for the underlying rotation calculations, which are done in radians, are the same, so when exceeding certain angles stuff will flip around). AE does not have "smart rotation", since it doesn't compare orientation (Quarternion) to rotation (Euler), it blindly only calculates the values. Other than using parented hierarchies you cannot circumvent this easily. Even expressions won't work in this case."


Echoes and Ribbons

You can use the echo effect to achieve a pseudo 3D ribbon of text which may be suitable for some types of animations. I say pseudo 3D because it cannot interact with other layers in 3D space. Basically, we will do all the same steps as above except we will not duplicate our Text_x comp. Instead, after bringing it into the Guide_x comp you simply apply the echo effect to the Text_x layer and adjust the settings in the effect controls; very easy compared to the true 3D version.

Well there you have it 3D ribbon text in After Effects; I hope you enjoyed this tutorial.

Dave Langley is a freelance visual effects, matte painting and motion graphics artist residing in Durham, New Hampshire.

Please discuss this technique in the After Effects forum at

Please visit our forums and view other articles at if you found this page from a direct link.