text that says Expressions Notepad

The Text Selector Expression is arguably the most elusive Adobe After Effects Feature and yet it’s its most powerful Text Feature

This blog aims to provide insights and samples into the mystical world of Text Expression Selectors. Readers are assumed to know how to commit to basic application and adjustments using the Text Tool.

Text Expression Selectors are like mysterious orbs in the sky that everyone sees but only few speak about.

With the Range and Wiggly Selectors, we take note of two critical observations –

(1) what gets selected is quite easy to manage and

(2) how each selection is affected by Text Animator Properties is not as easy to manage.

And it is the latter where the Text Expression Selector shines.

Before we proceed, let’s define Text Objects

Text Object : A group of one or more text characters defined by the Based On Parameter. The Based On Drop Menu provides 4 Group Types – Characters, Characters Excluding Spaces, Words and Lines.

You may also create your own Text Objects using any one of these as the basis for grouping.

With Range Selectors, how each Text Object is affected and how it animates involves a complex formula comprising of the parameters in the Range Selector and Advanced Sections of the Text Animator Group.

With the Expression Selector, it’s actually a lot easier to manage how Text Objects are affected by the Text Animator Property. The tricky part is that you have to write your own selection and animation functions which are built-into Text Expression selectors that others provide for free or as a paid product.

What you will want to take away from this blog is that Text Expression Selectors allow for more control over how each Text Object is affected by Text Animator Properties.

Let’s take a look at a few Text Expression Selector examples because showing by example is always a good way for visual stuff.

What I’d like you to take away from watching this video is to appreciate how text characters are selected and animated with Expression Selectors and how the results here differ from results obtained with using Range and Wiggly Selectors.

Some contents or functionalities here are not available due to your cookie preferences!

This happens because the functionality/content marked as “Google Youtube” uses cookies that you choosed to keep disabled. In order to view this content or use this functionality, please enable cookies: click here to open your cookie preferences.

Text Expression Selector Samples deployed using AeXpression Notepad, a soon-to-be-released After Effects Script UI.

Can’t see this video? Click here! If you are in Safari, hover over “Safari” in your menu, click “settings for creativecow.net” and toggle the “Auto-Play” to “Allow All Auto-Play”.

To add some brain work into the mix, pause at appropriate samples and ask yourself how you would re-create a sample using the Range and/or Wiggly Selectors.

Chances are you will not be able to replicate most, if any of these Text Animations if you used Range and/or Wiggly Selectors. And with this, the motivation is for you to start venturing into the world of Text Expression Selectors, if you have not done so.

Some contents or functionalities here are not available due to your cookie preferences!

This happens because the functionality/content marked as “Google Youtube” uses cookies that you choosed to keep disabled. In order to view this content or use this functionality, please enable cookies: click here to open your cookie preferences.

Samples of After Effects Expression Text Selector. These and many others will be released either together with AeXpressions NotePad or shortly thereafter.

Can’t see this video? Click here! If you are in Safari, hover over “Safari” in your menu, click “settings for creativecow.net” and toggle the “Auto-Play” to “Allow All Auto-Play”.

Text Selectors in After Effects

Most After Effects users are familiar with its Text Tool and its Range and Wiggly Selectors. However, only a tiny percentage have used the Text Expression Selector.

Advanced After Effects users will have used Expressions in their Range and Wiggly Selectors and even the Source Text Property. Now, let’s take a look at the Text Expression Selector.

Text Expression Selector

Default Text Expression Selector

When you first apply the Text Expression Selector to a Text Animator Group, this is the expression that is applied –

selectorValue * textIndex/textTotal

Unfortunately, for almost every user that sees this for the first time, it’s a cryptic line of code. So, let’s try to clear this up – what you see are essentially three reserved variables; they are functions built into the After Effects Expression Engine.

Before we proceed, let’s define these variables/functions –

selectorValue * textIndex/textTotal

selectorValue : This is defined as a percentage of the value of the Text Animator Property you apply to the Animator Group.

The selectorValue * ratio is calculated for each Text Object on every frame. I use two different ranges for the selectorValue : 0 and 100 or -100 and 100. They provide different results and you should experiment further appreciate their nuances.

The ideal way to experiment is to link/replace selectorValue with a Slider Control while leaving the other variables in the default expression as is.

There is yet another range that is useful and this accounts for the number of Text Objects available. As this is an introductory article, I will leave this use-case for later.

textIndex : This defines the index of a single Text Object regardless of the type of Text Object selected with the Based On Drop Menu. Each Text Object receives a distinct index.

textTotal : This is the total number of Text Objects based on the Based On Parameter.

Now, let’s proceed with some hands-on work …

Setting up the ground work

** Leave the Based On Parameter at its default selection, Characters.

** Disable the Range Selector by clicking on its Visibility Icon.

** Input the following as your Text Layer’s Input String –

1234567890

** Set the Text Layer’s Paragraph Justification to Left Aligned

** Apply the following Expression to the Text Layer’s Anchor Point

const myLayerRect = thisLayer.sourceRectAtTime();

[myLayerRect.left , myLayerRect.top + myLayerRect.height]

The last two procedures are applied only to ensure we have identical results – they have no bearing on results sitrctly occurring from the Text Selector Expression.

If we have a future blog on the Text Expression Selector, we will look at combining Range and Expression Selectors – for now, let’s focus on foundational topics.

With the default Text Expression Selector applied, add the Position Text Animator Property and set its xValue to 100.

If you applied the Position Text Animator Property, you will notice that the first Text Object is offset by 10 pixels and subsequent Text Objects are moved by 10 pixels * the index of the Text Object.

Let’s compare the Before & After…

Default Text Input String without a Text Animator Group.

The width of the Text Layer is 414 pixels at its default – no Text Selector is active.

Expression Selector applied with Text Animator Position Property set to [100,0 ].

The width of the Text Layer is 504 pixels with the Text Expression Selector active and the Position Text Animator Property set to [100,0].

Interesting result and a casual but useful observation …

So, the default Text Selector Expression applies the selectorValue of the Text Animator Property to Text Objects much like what the Range Selector does when its Shape Parameter is set to Ramp Up and its Offset value is set to 0.

Range Selector Expression with Text Animator Position Property set to [100,0] – Shape Type set to Ramp Up.

The width of the Text Layer is 504 pixels with the Text Expression Selector disabled and we use the Range Selector with its Shape Type set to Ramp Up and its Offset set to 0. The Position Text Animator Property is set to [100,0]. 

Let’s return focus to the Text Expression Selector …

Default Text Expression Selector

selectorValue * textIndex/textTotal

The default expression applied to the Text Expression Selector does not animate Text Objects. What the default expression does is distribute the selectorValue across the input text, weighted by each Text Object’s Index – smaller indices receive a smaller weighting (lower value) and Text Objects with larger indices receive a higher weighting (higher value).

To animate Text Objects, we animate at least one of the three variables/functions in the default expression.

Additionally, textTotal should not be 0 as this will result in an expression error – a divide by 0 error. Other than this restriction, you should be able to experiment with different static and keyframed values to better understand the mechanics of the Text Expression Selector.

A couple of Helper Notes

Link each of the three variable/functions to a Slider Control and experiment by changing the value of one Slider Control at a time.

Then, keyframe or set non-animating values for each variable/function.

Key Takeaway …

What I’d like you to keep an eye and your mind about is how you are able to control the value that is applied to Text Objects and the selection of Text Objects – they are independent when using the Text Expression Selector. This allows you to have greater control over what gets selected and and how they animate. Contrast with with using the Range Selector where it is a lot difficult to control how Text Objects animate.

If there is interest, I will dive deeper in a future Blog Post. I will also be launching a set of Text Expression Selectors. These will be distributed either as Animation Presets (FFX) or applied as Text Expression Selectors within an AEP.

Below are tutorials by three brilliant minds that should motivate you to tak a further look into at Text Expression Selectors. 

At the end, I share two Text Expression Selector expression 

** select Letters/Numbers of your Input String

** select one or more words/phrases of your Input String

The following tutorial is a good introduction and it’s got a few examples you can easily apply to your work.

Text animator’s Expression selector explained | Quick After Effects Tip

by ruthlessly quick AE tips

Some contents or functionalities here are not available due to your cookie preferences!

This happens because the functionality/content marked as “Google Youtube” uses cookies that you choosed to keep disabled. In order to view this content or use this functionality, please enable cookies: click here to open your cookie preferences.

Can’t see this video? Click here! If you are in Safari, hover over “Safari” in your menu, click “settings for creativecow.net” and toggle the “Auto-Play” to “Allow All Auto-Play”.

Before diving into Luis’ tutorial, you should dowload and take a look at his Expression to get an idea of what it does. His technique is excellent but he combines the use of the Text Expression Selector in ways which WILL confuse you if you are starting out in this area and you are not familiar with his Expression.

After Effects Rigging – Expression Selector

by Luis Martínez

Some contents or functionalities here are not available due to your cookie preferences!

This happens because the functionality/content marked as “Google Youtube” uses cookies that you choosed to keep disabled. In order to view this content or use this functionality, please enable cookies: click here to open your cookie preferences.

Can’t see this video? Click here! If you are in Safari, hover over “Safari” in your menu, click “settings for creativecow.net” and toggle the “Auto-Play” to “Allow All Auto-Play”.

Ilir takes matters into his own world. His Text Expression Selector Tutorials are quite extraordinary.

Advanced AE Expressions – Multiline Text Borders – Part 1 –

by Ilir Beqiri

Some contents or functionalities here are not available due to your cookie preferences!

This happens because the functionality/content marked as “Google Youtube” uses cookies that you choosed to keep disabled. In order to view this content or use this functionality, please enable cookies: click here to open your cookie preferences.

Can’t see this video? Click here! If you are in Safari, hover over “Safari” in your menu, click “settings for creativecow.net” and toggle the “Auto-Play” to “Allow All Auto-Play”.

Select single letter/number Expression

  • includes Case-Sensitive checkbox

This Expression is applied to the Amount Property of the Text Expression Selector.

This Expression allows you to select Individual Text Characters based on your input.

You can specify the character and its instance/occurrence in the Input Text String.

Each entry is “letter:occurrence”. If no occurrence is specified, all instances of that Letter/Number are selected. Use a comma to separate different search Letters/Numbers.

Sample Use-Case …

Search Array – “A:1”, “T”, “o” with Case-sensitive Checkbox enabled. Matches first instance of “A” and all instances of “o” and “T”.

Search Array – “A:1”, “T”, “o” with Case-sensitive Checkbox enabled. Matches first instance of “A” and all instances of “o” and “T”.

// START OF EXPRESSION

// ** === SET BASED ON TO Characters ===

// Checkbox for Case-Sensitive matching

var CaseSensitive = effect(“CaseSensitive”)(“ADBE Checkbox Control-0001”); // |v=0|

// === Your search terms here ===

var searchArray = [

“A:1”, // only the 1st “a” (upper or lower – depends on case-sensitive checkbox)

“b”, // all “b”s

“o” // all “o”s

];

var src = text.sourceText.toString();

var flat = [];

for (var i = 0; i < src.length; i++) {

var c = src.charAt(i);

if (c !== “\r” && c !== “\n”) {

flat.push(c);

}

}

var n = flat.length;

// Simplified matching: if case-sensitive, match exactly.

// Otherwise compare both characters lowercased.

function matchChar(c, term) {

if (CaseSensitive == 1) {

return (c === term);

} else {

return (c.toLowerCase() === term.toLowerCase());

}

}

var matched = [];

for (var t = 0; t < searchArray.length; t++) {

var entry = searchArray[t].trim();

if (!entry) continue;

var parts = entry.split(“:”);

var letter = parts[0];

if (parts.length > 1 && parts[1].trim()) {

var targetOccur = parseInt(parts[1], 10);

var count = 0;

for (var i = 0; i < n; i++) {

if (matchChar(flat[i], letter)) {

count++;

if (count === targetOccur) {

matched.push(i);

break;

}

}

}

} else {

for (var i = 0; i < n; i++) {

if (matchChar(flat[i], letter)) {

matched.push(i);

}

}

}

}

var idx0 = textIndex – 1;

(matched.indexOf(idx0) !== -1) ? 100 : 0;

// END OF EXPRESSION

// Copyright 2025 – Roland Kahlenberg

/*

 For personal and professional use. Not to be sold, resold, exchanged, shared on personal or publicly accessible sites

.*/

Select word/phrases Text Selector Expression

  • includes Partial Word Match Checkbox

Sample Use-Case …

Search Array – “Effect”, “practical advice”.

PartialWordMatch – unchecked

Sample Use-Case …

Search Array – “Effect”, “practical advice”.

PartialWordMatch – checked

// START OF EXPRESSION

/* This Expression is applied to the Amount Property of the Text Expression Selector.

This Expression allows you to select word(s)/phrases.

** You can specify the character and its instance/occurrence in the Input Text String.

** Partial Word Match is supported via a Checkbox.

** By default, the Expression ignores punctuation marks that follow the last word of a matched word/phrase.

** SET BASED ON TO Characters Excluding Spaces

** Literal Word Selection

*/

var PartialWordMatch = effect(“PartialWordMatch”)(“ADBE Checkbox Control-0001”) == 1;

var ExcludePunctuation = 1;

var SearchArray = [

“Effect” ,

“Note:2”

];

var SourceText = text.sourceText.toString();

var Lines = SourceText.split(“\n”);

var Words = [];

for (var l = 0; l < Lines.length; l++) {

var line = Lines[l].trim();

if (!line) continue;

var lineWords = line.split(/\s+/).filter(function(w){ return w; });

Words = Words.concat(lineWords);

}

var MatchedWordIndices = [];

var MatchMetadata = [];

for (var i = 0; i < SearchArray.length; i++) {

var entry = SearchArray[i].trim();

if (!entry) continue;

var parts = entry.split(“:”);

var phrase = parts[0];

var targetWords = phrase.split(/\s+/).filter(function(w){ return w; });

var last = targetWords.length – 1;

var trailingP = /[.,!?;:]$/.test(targetWords[last]);

var cleanWords = targetWords.map(function(w, idx){

return (ExcludePunctuation && !trailingP && idx === last)

? w.replace(/[.,!?;:]$/, “”)

: w;

});

function scan(limitNth) {

var count = 0;

for (var w = 0; w <= Words.length – targetWords.length; w++) {

var ok = true;

for (var tw = 0; tw < targetWords.length; tw++) {

var word = Words[w + tw];

var wordClean = (ExcludePunctuation && !trailingP && tw === last)

? word.replace(/[.,!?;:]$/, “”)

: word;

if (PartialWordMatch) {

var off = wordClean.indexOf(cleanWords[tw]);

if (off < 0) { ok = false; break; }

} else {

if (wordClean !== cleanWords[tw]) { ok = false; break; }

}

}

if (!ok) continue;

count++;

if (limitNth == null || count === limitNth) {

for (var tw = 0; tw < targetWords.length; tw++) {

var idxFlat = w + tw;

var word = Words[idxFlat];

var wordClean = (ExcludePunctuation && !trailingP && tw === last)

? word.replace(/[.,!?;:]$/, “”)

: word;

var off = PartialWordMatch

? wordClean.indexOf(cleanWords[tw])

: 0;

var length = PartialWordMatch

? cleanWords[tw].length

: wordClean.length;

MatchedWordIndices.push(idxFlat);

MatchMetadata.push({

wordIndex: idxFlat,

matchOffset: off,

matchLength: length

});

}

if (limitNth != null) break;

}

}

}

if (parts.length > 1 && parts[1].trim()) {

var n = parseInt(parts[1], 10);

if (!isNaN(n) && n > 0) scan(n);

} else {

scan(null);

}

}

var Boundaries = [];

var charIndex = 1;

for (var l = 0; l < Lines.length; l++) {

var line = Lines[l];

if (!line.trim()) continue;

var lineWords = line.split(/\s+/).filter(function(w){ return w; });

for (var w = 0; w < lineWords.length; w++) {

var word = lineWords[w];

var start = charIndex;

var fullLength = word.length;

charIndex += fullLength;

var end = start + fullLength – 1;

var from = Boundaries.length

? Boundaries[Boundaries.length – 1].wordIndex + 1

: 0;

var flat = Words.indexOf(word, from);

Boundaries.push({

start: start,

end: end,

wordIndex: flat

});

}

if (l < Lines.length – 1 && Lines[l+1].trim()) {

charIndex++;

}

}

var isMatch = false;

for (var b = 0; b < Boundaries.length; b++) {

var B = Boundaries[b];

var idx = B.wordIndex;

var mi = MatchedWordIndices.indexOf(idx);

if (mi === -1) continue;

var meta = MatchMetadata[mi];

var matchStart = B.start + meta.matchOffset;

var matchEnd = matchStart + meta.matchLength – 1;

if (textIndex >= matchStart && textIndex <= matchEnd) {

isMatch = true;

break;

}

}

isMatch ? 100 : 0

// END OF EXPRESSION

// Copyright 2025 – Roland Kahlenberg

/*

For personal and professional use. Not to be sold, resold, exchanged, shared on personal or publicly accessible sites. */


Using Luma Mattes in Adobe Premiere Pro
This very quick tutorial shows you how to take an RGB clip …
Stunning Starling Murmuration Tutorial
It's one of the greatest natural wonders and we're endlessly amazed by …

Enjoying the news? 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!

Sign up:

* indicates required

Responses

We use anonymous cookies to give you the best experience we can.
Our Privacy policy | GDPR Policy