Snake case properties
- After Effects User Guide
- Beta releases
- Getting started
- Workspaces
- Projects and compositions
- Importing footage
- Text and Graphics
- Text
- Motion Graphics
- Work with Motion Graphics templates in After Effects
- Use expressions to create drop-down lists in Motion Graphics templates
- Work with Essential Properties to create Motion Graphics templates
- Replace images and videos in Motion Graphics templates and Essential Properties
- Animate faster and easier using the Properties panel
- Drawing, Painting, and Paths
- Overview of shape layers, paths, and vector graphics
- Paint tools: Brush, Clone Stamp, and Eraser
- Taper shape strokes
- Shape attributes, paint operations, and path operations for shape layers
- Use Offset Paths shape effect to alter shapes
- Creating shapes
- Create masks
- Remove objects from your videos with the Content-Aware Fill panel
- Roto Brush and Refine Matte
- Layers, Markers, and Camera
- Animation, Keyframes, Motion Tracking, and Keying
- Animation
- Keyframe
- Motion tracking
- Keying
- Transparency and Compositing
- Adjusting color
- Effects and Animation Presets
- Effects and animation presets overview
- Effect list
- Effect Manager
- Simulation effects
- Stylize effects
- Audio effects
- Distort effects
- Perspective effects
- Channel effects
- Generate effects
- Time effects
- Transition effects
- The Rolling Shutter Repair effect
- Blur and Sharpen effects
- 3D Channel effects
- Utility effects
- Matte effects
- Noise and Grain effects
- Detail-preserving Upscale effect
- Obsolete effects
- Expressions and Automation
- Expressions
- Expression basics
- Understanding the expression language
- Using expression controls
- Syntax differences between the JavaScript and Legacy ExtendScript expression engines
- Editing expressions
- Expression errors
- Using the Expressions editor
- Use expressions to edit and access text properties
- Expression language reference
- Expression examples
- Automation
- Expressions
- Immersive video, VR, and 3D
- Construct VR environments in After Effects
- Apply immersive video effects
- Compositing tools for VR/360 videos
- Advanced 3D Renderer
- Import and add 3D models to your composition
- Import 3D models from Creative Cloud Libraries
- Image-Based Lighting
- Extract and animate lights and cameras from 3D models
- Tracking 3D camera movement
- Cast and accept shadows
- Embedded 3D model animations
- Shadow Catcher
- 3D depth data extraction
- Modify materials properties of a 3D layer
- Work in 3D Design Space
- 3D Transform Gizmos
- Do more with 3D animation
- Preview changes to 3D designs real time with the Mercury 3D engine
- Add responsive design to your graphics
- Views and Previews
- Rendering and Exporting
- Basics of rendering and exporting
- H.264 Encoding in After Effects
- Export an After Effects project as an Adobe Premiere Pro project
- Converting movies
- Multi-frame rendering
- Automated rendering and network rendering
- Rendering and exporting still images and still-image sequences
- Using the GoPro CineForm codec in After Effects
- Working with other applications
- Collaboration: Frame.io, and Team Projects
- Memory, storage, performance
- Knowledge Base
This document explains the expression language syntax differences between the JavaScript and Legacy ExtendScript expression engines in After Effects 16.0.
Refer to this document to learn how to improve your expressions for the JavaScript expression engine, or when fixing errors that occur when expressions written for previous releases of After Effects fail to evaluate in the JavaScript expression engine.
The expression language in After Effects is based on JavaScript, which is an implementation of ECMAScript. The JavaScript expression engine in After Effects 16.0 is based on ECMAScript 2018. The Legacy ExtendScript expression engine is based on ECMAScript 3 (1999). (Adobe ExtendScript is also the language used for scripting in After Effects and other Adobe applications.)
You can follow the examples provided below, as well as guidance for how to author expressions that work in both the JavaScript and Legacy ExtendScript expression engines.
The core differences between the JavaScript and Legacy ExtendScript expression engines are:
- Modern JavaScript syntax: Additions made to JavaScript in ECMAScript 5 through ECMAScript 2018 allow for new syntax and methods in expressions when using the JavaScript expression engine. Also, some small improvements have been made to the expression syntax when using the JavaScript expression engine. Here are some core differences:
- Incompatible legacy syntax: Some legacy syntax is not compatible with the JavaScript expression engine. These exceptions are documented here, along with the changes needed to make non-working syntax compatible with the JavaScript engine. Here are some core differences:
- if...else syntax differences
- if and else cannot be on the same line without brackets
- Expressions cannot end in a function declaration
- this() shorthand syntax is not allowed; use thisLayer() instead
- Source Text property array-index access to characters requires .value
- Snake case properties and methods are not allowed
- Using eval() with binary-encoded (.jsxbin) expressions
- Limited support for the $. (Dollar) object
- No support for ...reflect.properties, ...reflect.methods, and toSource()
- Syntax requirements for .jsx expression libraries and eval(): When using expressions inside a .jsx expression function library or when an expression is called inside eval(), thisLayer. and thisProperty. need to be explicitly called on, and math operations on arrays needs to be replaced with vector math. Here are some core differences:
Modern JavaScript syntax: Improvements made to the expression language
Expressions can use JavaScript syntax from ECMAScript 2018
Many additions have been made to the JavaScript language since ECMAScript 3. There are new methods to use with strings, arrays, and objects which are more compact and readable. There are also new ways to declare variables and functions, as well as default parameters, spread operators and more. This document does not cover these changes, as they are general to the JavaScript language. Resources for learning about the many syntax additions can be found at the following links:
- JavaScript ES5 Guide (aka ECMAScript 5)
- New Array methods
- New Object methods
- JSON.stringify() & JSON.parse()
Additional recommended, in-depth resources for learning JavaScript:
.value is no longer required when linking to other properties from Source Text
When referencing another property value from a Source Text property, the Legacy ExtendScript engine requires .value to be added at the end of the property. The JavaScript engine shows the value of the property by default unless another attribute like .propertyIndex or .name is explicitly used.
For example:
thisComp.layer("Solid 1").transform.position // In the Legacy engine, this evaluates to "[object Property]" when applied to a Source Text property. // In the JavaScript engine, this evaluates to the value of the Position property of layer "Solid 1" when applied to a Source Text property.
Freezing property values with posterizeTime(0)
In After Effects 16.0, posterizeTime(0) freezes the property value at time 0 in the composition. This applies to both the JavaScript and Legacy ExtendScript engines.
This is not backward-compatible and may cause unexpected results in After Effects versions prior to 16.0.
Incompatible Legacy Syntax
Nearly all of the expression syntax from the Legacy ExtendScript expression engine is forward-compatible with the JavaScript expression engine. However, there is some legacy syntax which is not compatible with the JavaScript expression engine. Sometimes this is caused by syntax changes in modern JavaScript. In other cases, obsolete or outdated syntax has been removed. Examples of non-working and working syntax are provided below.
Most of these syntax difference can be corrected via application scripting that re-writes the expressions.
if...else syntax differences
In general, it is recommended to always write if...else statements with line breaks and brackets in accordance with MDN guidelines. The Legacy ExtendScript engine was tolerant of loose syntax in if...else statements, however the JavaScript engine is strict. Incorrect if...else syntax fails to evaluate when using the JavaScript engine.
Ending an expression in an if statement without an else is not allowed
When an expression ends with an if statement without an else statement, the JavaScript engine will fail to evaluate the expression with the error "Undefined value used in expression (could be an out of range array subscript?)". In the Legacy ExtendScript engine, the following expression evaluates to 100 if the time is greater than 1 second, else it evaluates to 50:
var x = 50; if ( time > 1 ) { x = 100 } // The "else" here is implied but not stated.
The JavaScript engine needs the else portion of the statement to be explicitly stated if it is the last statement in the expression:
var x = 50; if ( time > 1 ) { x = 100; } else { x; }
This will evaluate correctly in both the JavaScript engine and the Legacy ExtendScript engine.
if and else cannot be on the same line without brackets
An if...else statement on a single line without brackets evaluates in the Legacy ExtendScript engine, but fails to evaluate in the JavaScript engine with an error such as "Syntax Error: Unexpected token else" or "Undefined value used in expression (could be an out of range array subscript?)". The error varies, depending on the context and property type.
In the Legacy ExtendScript engine, the following expression evaluates to 100 if the time is greater than 1 second, else it evaluates to 50:
if ( time > 1 ) 100 else 50;
The JavaScript engine requires line breaks or brackets to evaluate if...else statements. For simple cases, the ternary operator may be used instead. Any of the following syntax can be used with the JavaScript engine:
// Solution A: adding a line break before "else" will allow both engines to evaluate correctly. if ( time > 1 ) 100 else 50; // Solution B: adding correct bracketing will also allow both engines to evaluate correctly. if ( time > 1 ) { 100 } else { 50 }; // Solution C: Use a ternary operator in place of the if...else statement, which also evaluates correctly in both engines. time > 1 ? 100 : 50;
All of the above solutions evaluate correctly in both the JavaScript engine and the Legacy ExtendScript engine.
Expressions cannot end in a function declaration
If an expression ends with a function declaration, the JavaScript engine fails to evaluate the expression with the error "Object of type found where a Number, Array, or Property is needed". In the JavaScript engine, the last item evaluated must return a value, rather than declare one.
The following example works in the Legacy engine, but not the JavaScript engine:
timesTen( value ); // The Legacy engine evaluates this line, even though the function is declared below. function timesTen ( val ) { return val * 10 }
When a function is called as the last line (instead of the declaration), the expression will evaluate correctly in both engines:
function timesTen ( val ) { return val * 10 } timesTen( value ); // The JavaScript engine needs the function call to happen below the declaration in order to return the correct value.
this() shorthand syntax is not allowed; use thisLayer() instead
In the Legacy ExtendScript engine, this was allowed to be used as shorthand for thisLayer. In the JavaScript engine, this refers to the global object and thisLayer must be used instead. Using this in the JavaScript engine will usually result in the error "this is not a function".
In the following Legacy ExtendScript example, this is used to create a compact link to a Text Layer Position property from the Source Text property:
this(5)(2).value;
In the JavaScript engine, this must be replaced by thisLayer:
thisLayer(5)(2).value;
Using thisLayer is compatible with both expression engines.
Source Text property array-index access to characters requires .value
In the Legacy ExtendScript expression engine, the characters of a text property could be accessed with bracket notation like an array:
text.sourceText[0] // Returns the first character of the Source Text property's text value.
In the JavaScript engine, .value must be added in order to access the characters:
text.sourceText.value[0] // Returns the first character of the Source Text property's text value.
This syntax is compatible with both engines.
Snake case properties and methods are not allowed
The deprecated snake case properties and methods (written with an underscore instead of camelCase) are not supported by the JavaScript engine. The camelCase versions should be used instead as they are compatible with both engines. The following is a list of the deprecated snake case and their corresponding camelCase.
|
camelCase properties |
Snake Case methods |
camelCase Methods |
---|---|---|---|
this_comp this_layer this_property color_depth has_parent in_point out_point start_time has_video has_audio audio_active anchor_point audio_levels time_remap casts_shadows light_transmission accepts_shadows accepts_lights frame_duration shutter_angle shutter_phase num_layers pixel_aspect point_of_interest depth_of_field focus_distance blur_level cone_angle cone_feather shadow_darkness shadow_diffusion active_camera |
thisComp thisLayer thisProperty colorDepth hasParent inPoint outPoint startTime hasVideo hasAudio audioActive anchorPoint audioLevels timeRemap castsShadows lightTransmission acceptsShadows acceptsLights frameDuration shutterAngle shutterPhase numLayers pixelAspect pointOfInterest depthOfField focusDistance blurLevel coneAngle coneFeather shadowDarkness shadowDiffusion activeCamera |
value_at_time() velocity_at_time() speed_at_time() nearest_key() posterize_time() look_at() seed_random() gauss_random() ease_in() ease_out() rgb_to_hsl() hsl_to_rgb() degrees_to_radians() radians_to_degrees() from_comp_to_surface() to_comp_vec() from_comp_vec() to_world_vec() from_world_vec() to_comp() from_comp() to_world() from_world() temporal_wiggle() loop_in_duration() loop_out_duration() loop_in() loop_out() |
valueAtTime() velocityAtTime() speedAtTime() nearestKey() posterizeTime() lookAt() seedRandom() gaussRandom() easeIn() easeOut() rgbToHsl() hslToRgb() degreesToRadians() radiansToDegrees() fromCompToSurface() toCompVec() fromCompVec() toWorldVec() fromWorldVec() toComp() fromComp() toWorld() fromWorld() temporalWiggle() loopInDuration() loopOutDuration() loopIn() loopOut() |
Using eval() with binary-encoded (.jsxbin) expressions
Expressions encoded in the ExtendScript binary format (saved as a binary .jsxbin file from ExtendScript ToolKit CC) are not supported by the JavaScript engine.
If you want to obfuscate an expression, use the Legacy ExtendScript engine or use a different obfuscation method that is compatible with ECMAScript 2018. Some obfuscation methods might not be compatible with both expression engines.
Limited support for the $. (Dollar) object
The $. (Dollar) object methods and properties are specific to ExtendScript and mostly not supported in the JavaScript engine. This table lists unsupported and supported uses of the $. (Dollar) object:
Unsupported $. |
Supported $. |
---|---|
$.fileName $.hiResTimes $.stack $.evalFile() $.list() $.setenv() $.getenv() $.appEncoding $.buildDate $.decimalPoint $.dictionary $.error $.flags $.includePath $.level $.line $.locale $.localize $.memCache $.os $.screens $.strict $.version |
$.build $.engineName (this is unsupported in the Legacy ExtendScript engine) $.global |
No support for ...reflect.properties, ...reflect.methods, and toSource()
reflect.properties and reflect.methods are not supported in the JavaScript engine; they are ExtendScript-specific methods which have no direct equivalent in JavaScript.
toSource() in JavaScript has been deprecated and is not part of any standards track.
To see a list of available properties and methods for any given After Effects property similar to what was provided by the above methods, use the following expression on a Source Text property and link it to your desired property, for example, using the pick whip in place of thisProperty on line 1:
let obj = thisProperty; // Replace "thisProperty" with a property-link to your desired property. let props = []; do { Object.getOwnPropertyNames(obj).forEach(prop => { if (props.indexOf(prop) === -1) { props.push(prop); } }); } while (obj = Object.getPrototypeOf(obj)); props.join("\n"); // Returns an array of strings listing the properties and methods available.
The above expression is not compatible with the Legacy ExtendScript engine. It uses syntax and methods not available in ECMAScript 3.
Syntax Requirements for .jsx Expression Libraries and eval() with the JavaScript Engine
When using expressions inside a .jsx expression function library or when an expression is called inside eval(), certain syntax needs to be modified:
An explicit thisLayer. or thisProperty. prefix needs to be added to any native method or attribute which is not explicitly called on a layer or property. The prefix tells the JavaScript engine which object you are calling the method or attribute on.
Math operations on array values like Position needs to be calculated using vector math or by using looping functions to act on every item in the array. Overloaded math operators like position + [100,100] will not evaluate.
When using the JavaScript engine, expressions are pre-processed before evaluation in order to make some of the Legacy ExtendScript expression syntax readable by the new engine. However, these pre-processing tasks are not performed when evaluating expressions from a .jsx expression function library or when an expression is called inside eval(). The above syntax modifications must be made manually for these cases. All of these syntax modifications are backward-compatible with the Legacy ExtendScript engine, so a .jsx expression library written to work with the JavaScript engine will also work with the Legacy ExtendScript engine.
Performance tip: Due to the lack of pre-processing, calling complex expressions from a .jsx library with this syntax and the JavaScript engine may see a performance improvement, compared to calling the same expression directly on a property.
Explicitly prefix native methods and attributes with thisLayer. or thisProperty.
The following table lists the methods and attributes that require a prefix. For example, time must be written as thisLayer.time, while wiggle() must be written as thisProperty.wiggle().
These prefixes are only required in cases when the attribute or method isn't already being explicitly called on another layer or property. For instance, when calling thisComp.layer(1).hasParent, adding thisLayer. is not required because .hasParent is already being explicitly called on layer(1).
Methods Requiring thisLayer. |
Attributes Requiring thisLayer. |
Methods Requiring thisProperty. |
Attributes Requiring thisProperty. |
---|---|---|---|
comp() |
time |
valueAtTime() |
velocity |
Replacing math operators with vector math functions
Both the JavaScript and LegacyExtendScript engines allow overloading math operators for arrays by using syntax like position + [100,100], but this does not work for expressions inside a .jsx expression function library or inside eval().
In order to perform math on array properties like Position, Scale, etc., the vector math equivalents should be used for addition, subtraction, multiplication, and division. The vector math functions will also work for regular numbers, so a function which might be called on properties of either data type should use the vector math functions.
The thisLayer. prefix must be used with the vector math functions.
- Addition: thisLayer.add(vec1, vec2)
- Subtraction: thisLayer.sub(vec1, vec2)
- Multiplication: thisLayer.mul(vec, amount)
- Division: thisLayer.div(vec, amount)
Below are examples of expressions using standard math and updated vector math. The vector math expressions also use the appropriate thisLayer. or thisProperty. prefix when needed.
To find the difference between a wiggle() and the value of a Position property:
// Standard Math: wiggle() - value; // Vector Math: thisLayer.sub( thisProperty.wiggle(), value );
To interpolate between two values, similar to linear(), but with an extended range beyond the defined minimum and maximum:
// Standard Math: value1 + ( ( t - tMin ) / ( tMax - tMin ) ) * ( value2 - value1 ); // Vector Math: thisLayer.add( value1, thisLayer.mul( thisLayer.div( thisLayer.sub( t, tMin ), thisLayer.sub( tMax, tMin ) ), thisLayer.sub( value2, value1 ) ) );
To loop a Position property in and out:
// Standard Math: loopIn( "cycle" ) + loopOut( "cycle" ) - value; // Vector Math: thisLayer.sub( thisLayer.add( thisProperty.loopIn( "cycle" ), thisProperty.loopOut( "cycle" ) ), value );