Expression Variables
Variables in expressions are powerful tools for sharing values between multiple expressions. They are also very useful to split up complex expressions into more easily understood and manageable chunks.
Variables are a named storage location for a value to be used by expressions. In order to have a place to store the value represented by a variable, they need to be defined. Variable definition can happen in a number of different ways.
Note that variables are not considered defined if they were only evaluated on an earlier frame. Variables are defined as the frame progresses within Designer, which means that things that happen earlier in a frame (such as device evaluation) cannot access variables defined later in the frame (such as during layer composition.) This is especially true when working with stacks of Layers - Layers can only define variables which are used by other Layers which would composite on top of the Layer which defined a variable.
Variable definition locations
Global constants
While these are technically variables, these constants do not change.
pi
- the mathematical constant π, often used with trigonometric functions.e
- the mathematical constant e (Euler’s constant), often used with mathematical functions.
Global system variables
Designer defines one variable which is accessible everywhere.
uptime
- the time of the current frame, which is synchronized across all servers and is therefore equal (within approximately 2ms) in a multi-machine session. This is also referred to as frame time in some places.
Local system variables
Some variables are defined only for certain contexts. For example when working with layers, the self
variable is defined to be the current value of the animated property the expression is controlling.
Variable assignments
When evaluating an expression, it is possible to save the value it evaluates to, as well as using it. For example, the user may enter a complex expression for brightness, but then want to share it across multiple layers. They could enter myvar = sin(uptime / 20)
, and then, in another layer (which must appear later in the composition stack) they could then set brightness to be simply myvar
.
It is important to note that the variable is only defined for as long as the layer (or other expression which defines the variable) is active. As soon as the layer becomes inactive (i.e. not under the play head) the variable is no longer defined, and references to it will no longer be valid.
In order to reference the variables defined by an assignment, the user must ensure the layer is below the user of the variable in the composition stack. i.e. The variables defined by variable assignment are only available to layers which would composite on top of the defining layer. Similarly, if the assignment is performed by some other Resource in the system, then the variable is defined only after that Resource has run within the frame.
ExpressionVariable layers
There are cases where the user wishes to create animated variables on the timeline, but does not want to explicitly tie them to a layer which is used for content (perhaps they are concerned that the layer may be moved or otherwise reprogrammed without consideration of the variable.)
The user can create an ExpressionVariable layer as they would any other layer. The configuration section allows them to create new variables and change their properties, and then animate them as they would any other property on a layer in the timeline.
In order to reference the variables defined by an ExpressionVariable layer, the user must ensure the layer is below the user of the variable in the composition stack. i.e. The variables defined by ExpressionVariable layers are only available to layers which would composite on top of the ExpressionVariable layer.
ExpressionVariable devices
In some cases, the variable the user wants to define is more global, and isn’t limited by a specific location on the timeline or does not need to be animated.
An ExpressionVariable device can be created in the device manager and used to create variables there.
Variable definition behaviour
Variables are defined every frame by the various locations described above.
If those locations are no longer run, the variable is no longer defined, and expressions which used it are no longer valid. An example is when a layer which defined a variable is not under the play head anymore, or a device is removed from the device manager.
If there are multiple locations which define the same variable on a frame, the second location which attempts to define the variable will be in an error state, and be unable to define the variable. It will indicate where the variable was first defined, for the user to determine how to resolve this conflict.
Variable naming
Variables must be named according to the syntax of the expression language. Some key points are:
- Case-sensitivity - variables named
hello
andHello
are distinct variables which can have very different or even incompatible values (e.g. a number or text) - No special characters - variables must start with an underscore (
_
) or an English alphabet letter, and consist of letters, underscores or numbers thereafter. This means no spaces or other special characters are valid. Unfortunately, at present this means non-English characters are not supported.
Users should also take care to use a consistent naming scheme when building projects with many variables. It is easy to create many variables with different meanings and purposes. Organization using devices and layers are useful for grouping variables into common locations for definition.
Function variables
Sometimes, when working with more complex or verbose logic functions such as if
, it is useful to break out parts to be reused or simply to make the process of reading the expression easier.
For example one use of function variables is to return the result of accessing a Resource using getByUID
:
To build a function variable, the writer should create an expression variables device or layer. A variable defined this way can be switched to Function type, and the desired expression can be written.
Copy the UID of the Layer (or other Resource) as required and write the getByUID
expression. There is no need to access a specific property, but that may make reusing the function elsewhere easier. The writer should consider what they are attempting to abstract when creating the function variable. In this example, the function textLayer
will return the chosen layer.
When using the function variable, the call syntax is required to evaluate the function, in this case that is textLayer()
which evaluates to the same value as the expression within the function variable. When returning Resources, this means that properties can be accessed directly from the returned value. If the expression evaluates to a number or text, it can be used directly as-is.
Context dependent evaluation
Functions are evaluated in the context of the calling expression. This means that the function variable can use a variable or other context which is defined after the function variable is, as long as the calling expression has access to that variable.
One useful implication of this is that function variables can access the self
variable. self
has its normal meaning according to the location the function is evaluated at. This can be used for example when working with OSC expressions in function variables. The following function expression can be written:
if(valid_osc:my.address, osc:my.address, self)
- this uses normal sequencing until a value is received on the OSC address /my/address
.