Dynamic Parameters
Dynamic parameters provide a way to turn a simple cue into something which changes over time and space, or reacts to operator input. Most cues which take simple values can also take dynamic parameters which evaluate to those values.
Frame Dynamic Parameters
When creating a dynamic parameter, you can control how dynamic it is.
The most dynamic, frame dynamic, are evaluated anew for every single
frame of control values that are sent out to the lights. If, on the
other hand, you want the value to be calculated when the effect is
started, and then stay constant, you can create the parameter with a
false
value for frame-dynamic?
Variable Parameters
Parameters can get their values from a show variable. Any number of values can be stored in the show, by assigning them a keyword with the function:
(afterglow.show/set-variable! :key value)
In addition to manually setting values in show variables, you can use MIDI mappings to have them set by turning a knob or sliding a fader on a MIDI control surface. Cues can also set variables, and can bind them to encoders or pressure sensitive pads on controllers like the Ableton Push and Novation Launchpad Pro. |
You can then refer to that stored value whenever you are building a
dynamic parameter (for example when you are passing in the :hue
parameter for a Color Parameter
as described below), by simply using the keyword (in this example,
:key
), and the current value stored in the show variable will be
used.
This keyword shorthand is just a convenience mechanism provided by
functions like
build-color-param
.
You can also explicitly build a reference to a show variable like
this:
(afterglow.effects.params/build-variable-param :key)
The most common reason you might want to use the explicit creation function is that you can control details about the binding by passing in optional keyword parameters:
Parameter | Default | Purpose |
---|---|---|
|
|
The type of value that is expected in the variable |
|
|
The value to assign the parameter if the variable is missing or has the wrong type |
|
none |
If supplied, is called with the value of the show variable, and its return value is used as the value of the parameter |
|
|
Whether the variable value should be looked at at every frame, or just once |
As an example of using these parameters, consider:
(afterglow.effects.params/build-variable-param
:key :type Number :default 0 :transform-fn #(* % 2))
This would cause the dynamic parameter to have a numeric value which is
twice the value found in the show variable named :key
.
When you use the keyword shorthand to bind to a show variable in building another dynamic parameter, the type and default are assumed from the context in which you are using the variable.
Another time when you will need to use this explicit way of
building a dynamic parameter from a show variable is if the parameter
holds keywords. In that case, clearly Afterglow can’t assume that a
keyword is shorthand for a show variable. The :interval parameter
for Afterglow’s built in oscillator-building functions, like
afterglow.effects.oscillators/sine ,
is an example of such a parameter: It expects the value :beat ,
:bar , or :phrase . So to have an oscillator look up and use the
value in the show variable :my-interval to control the interval over
which it oscillates, you would write something like this:
|
(afterglow.effects.oscillators/sine
:interval (afterglow.effects.params/build-variable-param
:my-interval :type clojure.lang.Keyword :default :beat))
Oscillated Parameters
Oscillated parameters vary over time, at a speed controlled by a
Metronome (usually the main show metronome,
synced to DJ equipment) and so can make the lights appear to be
reacting intelligently to the music being played. The timing
information produced by the metronome is fed into an
Oscillator, which determines the shape of
the wave that controls its value, and the frequency at which it
oscillates, related to beats, bars, or phrases of the underlying
metronome. The resulting value can then be scaled to meet the needs of
whatever is being generated (a dimmer level, color component, or light
rotation). Oscillated parameters are created by calling
afterglow.effects.oscillators/build-oscillated-param
.
(afterglow.effects.oscillators/build-oscillated-param oscillator)
The oscillator
parameter is an
Oscillator created by one of the functions
in
afterglow.effects.oscillators
which, as described above, determines how to react to the time
information provided by the metronome. Additionally, you can supply
one of the following optional keyword parameters:
Parameter | Default | Purpose |
---|---|---|
|
|
The smallest value that the oscillated parameter will be assigned |
|
|
The largest value that the oscillated parameter will be assigned |
|
none |
If supplied, is used instead of the main show metronome |
|
|
Whether the parameter should be calculated at every frame, or just once |
The keyword parameters :min
, :max
, and :metronome
can themselves
be bound to show variables by passing in the keyword with which the show
variable was created. The frame-dynamic setting of such variable
bindings will be controlled by the frame-dynamic setting of the
oscillated parameter being created.
If :min ever evaluates greater than or equal to :max , the
oscillator will be pinned to the value of :min .
|
Step Parameters
Like oscillated parameters (above), step parameters vary over time, at
a speed controlled by a Metronome (usually
the main show metronome, synced to DJ equipment). But rather than
moving back and forth, step parameters increase steadily over time,
because they are designed to control the progression of a
chase. Step parameters are created by calling
afterglow.effects.params/build-step-param
.
(afterglow.effects.params/build-step-param)
With no arguments, this creates a step parameter that starts out with
the value 1
for the duration of the beat closest to when you created
it, and the value will jump up by one as each subsequent beat occurs:
If a less-abrupt transition between stages in the chase is desired, a
fade can be added between them by passing a value with the optional
keyword argument :fade-fraction
. When omitted, the default value is
0
, meaning no time is spent fading, which results in the kind of
abrupt steps seen in the graph above. Passing a value of 0.2
would
cause the parameter to spend 1/5 of its time fading: During the final
0.1 of the beat, it would ramp up towards the midpoint of the next
value, and then finish that ramp during the first 0.1 of the next
beat, as shown in the following graph:
(afterglow.effects.params/build-step-param :fade-fraction 0.2)
The graph shows that most of each beat is spent with the step parameter steady at its expected value, but the first and last tenths are a linear fade from and to the next value. Changing the fade fraction to 0.5 causes half the time to be spent fading, and only half sitting at the beat’s assigned value:
(afterglow.effects.params/build-step-param :fade-fraction 0.5)
That trend continues until the maximum possible fade-fraction value of
1
is used, which causes all of each beat to be spent fading, so the
step parameter continuously fades through values, reaching the value
assigned to a given beat at the midpoint of that beat:
(afterglow.effects.params/build-step-param :fade-fraction 1)
In addition to linear fades, you can smooth out the start and end of
the fades by using a sine-shaped fade curve, by passing the optional
keyword argument :fade-curve
with the value :sine
. Here is what
that looks like with a continuous fade:
(afterglow.effects.params/build-step-param :fade-curve :sine :fade-fraction 1)
The smoothing effect of the sine curve option becomes even more evident when you configure the step parameter to fade for only part of the beat:
(afterglow.effects.params/build-step-param :fade-curve :sine :fade-fraction 0.5)
Of course, as the amount of time spent fading gets compressed, the smoothing is less obvious, although it is still there. Dropping back to fading over just the first and last tenth of the beat looks like this:
(afterglow.effects.params/build-step-param :fade-curve :sine :fade-fraction 0.2)
When the fade fraction is 0
, it does not matter what the fade curve
is, because no fading takes place.
You can also have the step parameter increment for each bar or phrase,
rather than each beat, by passing the optional keyword argument
:interval
with the value :bar
or :phrase
. And, as with
oscillators, you can use the optional keyword argument
:interval-ratio
to have the parameter run at the specified fraction
or multiple of the chosen interval. The way that :interval-ratio
works is illustrated in the Ratios section of
the oscillator documentation.
As one example of :inteval-ratio
specifically applied to step
parameters, here is what the preceding graph would look like if the
interval ratio was changed to a value of one half, meaning that the
step parameter increases every half of a beat:
(afterglow.effects.params/build-step-param :interval-ratio (/ 1 2)
:fade-curve :sine :fade-fraction 0.2)
Finally, if you would like the beat numbers to be counted from a time
that is different than when you created the step parameter, you can
pass a metronome snapshot along with the keyword argument :starting
,
and beats will be counted so that the first beat is the one that
occured closest to that snapshot.
For maximum flexibility, any of the parameters to build-step-param
can themselves be dynamic parameters from the show. If none of them
are, a more efficient version of the step parameter is built,
precalculating as much as possible.
Color Parameters
Color parameters are an extremely flexible way of dynamically assigning
color. The basic way to create one is to call
afterglow.effects.params/build-color-param
.
(afterglow.effects.params/build-color-param)
By itself this call would simply return a non-dynamic black color. However, you will use one or more of the following optional keyword parameters to get the dynamic color you want:
Parameter | Default | Purpose |
---|---|---|
|
black |
The base, starting color of this dynamic color |
|
|
Red brightness, from 0 to 255 |
|
|
Green brightness, from 0 to 255 |
|
|
Blue brightness, from 0 to 255 |
|
|
Hue value, from 0.0 to 360.0 |
|
|
Saturaion value, from 0.0 to 100.0 |
|
|
Lightness value, from 0.0 to 100.0 |
|
|
Hue shift value, from -360.0 to 360.0 |
|
|
Saturation shift value, from -100.0 to 100.0 |
|
|
Lightness shift value, from -100.0 to 100.0 |
|
|
Whether the parameter should be calculated at every frame, or just once |
All of these parameters, except for frame-dynamic
, can themselves be
dynamic parameters, such as show variables
(with the convenience shorthand of just passing in the keyword by which
the show variable was stored) or oscillated
parameters.
Refer to Working with Color for a refresher on the meaning of the basic color components. It would not make sense to pass all of these parameters, because some will override others, but here is how they are evaluated:
-
The base color is established by the
:color
parameter. -
If any of
:r
,:g
, or:b
have been supplied, the color is replaced by creating an RGB color with the values (or defaults) supplied. -
If any of
:h
,:s
, or:l
have been supplied, the color is replaced by creating an HSL color with the values (or defaults) supplied. -
If
:adjust-hue
was supplied, the hue of the color obtained so far is shifted by adding that amount to it (and wrapping around the color circle if needed). -
If
adjust-saturation
was supplied, the saturation of the color is adjusted by adding that amount to it, maxing out at 100.0, and bottoming out at 0.0. Lower saturations yield less colorful (more gray) colors. -
If
adjust-lightness
was supplied, the lightness of the color is adjusted by adding that amount to it, maxing out at 100.0, and bottoming out at 0.0. A lightness of 50.0 allows for a fully saturated color, lightnesses above that start getting whitened, and a lightness of 100.0 is pure white; lightnesses below 50.0 start getting darkened, and a lightness of 0.0 is pure black.
Finally, the result of all this is the color that is returned by the dynamic parameter. Afterglow tries to be as efficient about this as possible, and do as much calculation as it can when the parameter is created. If there are no frame dynamic parameters, it will return a fixed color. But you can easily use frame-dynamic oscillated parameters and get lovely shifting rainbow cues, as shown in the effect examples.
Movement
There are three different kinds of parameters which tell fixtures how to move. They differ in the way that you express direction or aim.
Direction Parameters
Direction parameters are one way to tell a group of fixtures to point
in a particular direction, or move in unison or in a coordinated
pattern. They are used with Direction
Effects. (Pan Tilt Parameters and
Pan/Tilt Effects are the other way to
achieve that result.) The basic way to create a direction parameter is
to call
afterglow.effects.params/build-direction-param
.
(afterglow.effects.params/build-direction-param)
By itself this call would simply return a non-dynamic direction telling fixtures to point directly at the audience. However, you will use one or more of the following optional keyword parameters to get the dynamic direction you want:
Parameter | Default | Purpose |
---|---|---|
|
|
The amount the light should point towards audience’s right |
|
|
The amount the light should point up |
|
|
The amount the light should point towards the audience |
|
|
Whether the parameter should be calculated at every frame, or just once |
Collectively, x
, y
, and z
specify a three-dimensional vector in
the light show’s frame of reference telling
the lights which direction they should point. The absolute magnitudes
of the values are not important, it is their relative sizes that
matter. The default of [0, 0, 1]
means the lights point neither left
nor right, neither up nor down, and straight towards the audience.
[1, 0, 0]
would be straight right, [-1, 0, 0]
straight left, [0,
1, 0]
straight up, and [0, 1, -1]
up and away from the audience at
a 45° angle. When this vector is supplied to a
Direction Effect, it causes the attached
lights to make the specified movement, if they are capable.
All of these parameters, except for frame-dynamic
, can themselves be
dynamic parameters, such as show variables
(with the convenience shorthand of just passing in the keyword by which
the show variable was stored) or oscillated
parameters.
Aim Parameters
Aim parameters are a way to tell a group of fixtures to aim at a particular point in space, or track something in unison or in a coordinated pattern. They are used with Aim Effects. The basic way to create one is to call:
(afterglow.effects.params/build-aim-param)
By itself this call would simply return a non-dynamic point telling fixtures to aim directly at a height of zero, centered on the X axis, two meters towards the audience. However, you will use one or more of the following optional keyword parameters to get the dynamic target point you want:
Parameter | Default | Purpose |
---|---|---|
|
|
How many meters along the X axis the target point is found |
|
|
How high up or down the Y axis is the target point |
|
|
How far towards or away from the audience is the target point |
|
|
Whether the parameter should be calculated at every frame, or just once |
Collectively, x
, y
, and z
specify a three-dimensional point
within the light show’s frame of reference telling the
lights where to aim. When this vector is supplied to an
Aim Effect, it causes the attached lights to
make the specified movement, if they are capable.
If you need to convert inches or feet to meters, which are the
standard distance units in Afterglow, you can use
afterglow.transform/inches
and
afterglow.transform/feet
.
All of these parameters, except for frame-dynamic
, can themselves be
dynamic parameters, such as show variables
(with the convenience shorthand of just passing in the keyword by which
the show variable was stored) or oscillated
parameters.
Pan/Tilt Parameters
A more traditional way of aiming fixtures (in contrast to Direction Parameters) involves setting pan and tilt angles. Afterglow supports this approach as well, although even in this case you use angles expressed in the standard show frame of reference regardless of how the individual fixtures are hung. Pan Tilt parameters work with Pan/Tilt Effects. The basic way to create one is to call:
(afterglow.effects.params/build-pan-tilt-param)
By itself this call would simply return a non-dynamic pan-tilt parameter telling fixtures to point directly at the audience. However, you will use one or more of the following optional keyword parameters to get the dynamic angles you want:
Parameter | Default | Purpose |
---|---|---|
|
|
How many degrees counter-clockwise should the light turn around the Y axis |
|
|
How many degrees counter-clockwise should the light turn around the X axis |
|
|
Supply a |
|
|
Whether the parameter should be calculated at every frame, or just once |
The rotations requested by pan
and tilt
jointly identify the
direction the light should turn away from the audience. The result of
the parameter is a pair of pan and tilt angles away from the z
axis
of the light show’s frame of reference
telling the lights which direction they should point. When this parameter
is supplied to a Pan/Tilt Effect, it
causes the attached lights to make the specified movement, if they are
capable.
Note that although internally Afterglow works with angles expressed in
radians, the values of pan
and tilt
are assumed to be in degrees
and will be converted to radians for the convenience of users who are
more accustomed to working with angles expressed in degrees. If you
would rather stick with radians, you can suppress this conversion by
passing a true
value with the :radians
keyword.
All of these parameters, except for frame-dynamic
, can themselves be
dynamic parameters, such as show variables
(with the convenience shorthand of just passing in the keyword by which
the show variable was stored) or oscillated
parameters.
You can also create a direction parameter using pan and tilt
angles if you want to work with Direction
Effects in those terms. This can be helpful, for example, when you
want to fade between a specific direction that is easiest to express
as a spatial vector, and one that is easiest to express in terms of
angles. Use
build-direction-from-pan-tilt
to create a normal direction parameter starting from the same pan/tilt
parameters described above.
|
Spatial Parameters
Spatial parameters allow you to base an effect parameter on the physical
arrangement or relationships between fixtures in your light show. The
way to create one is to call
afterglow.effects.params/build-spatial-param
.
(afterglow.effects.params/build-spatial-param fixtures-or-heads f)
The required parameters are the fixtures and/or heads over which you
want this parameter to be calculated, and a function which, when invoked
with a fixture or head, returns a number or a dynamic Number
parameter.
If desired, the results returned for all included heads can be scaled
to fall within a standard range. Scaling is activated using the
optional keyword parameters :max
and :min
. If neither is supplied,
scaling is not performed. Passing a value for only :max
activates
scaling with a default minimum value of 0
, and passing a value for
only :min
activates scaling with a default maximum value of 255
.
The maximum value must be larger than the minimum value.
Parameter | Default | Purpose |
---|---|---|
|
n/a |
If present, activates result scaling, and establishes the smallest value this dynamic parameter will hold. |
|
n/a |
If present, activates result scaling, and establishes the largest value this dynamic parameter will hold. |
|
n/a |
Whether the parameter should be calculated at every frame, or just once. |
As noted above, the values returned by f
can themselves be
dynamic parameters, such as show variables
(with the convenience shorthand of just passing in the keyword by which
the show variable was stored) or oscillated
parameters. If frame-dynamic
is not explicitly set, the spatial
parameter will be frame dynamic if any value returned by f
is
frame-dynamic.
Useful things that f
can do include calculating the distance of the
head from some point, either in 3D or along an axis, its angle from
some line, and so on. These can allow the creation of lighting
gradients across all or part of a show. Spatial parameters make
excellent building blocks for color,
direction and aim
parameters, as shown in the
effect examples.
Combining Parameters
Sometimes you want to build a cue parameter by combining some other
values using a simple expression. While you can certainly do this by
implementing the low-level
IParam
protocol,
Afterglow provides a helper function,
build-param-formula
to eliminate most of the boilerplate involved in that approach. You
can see an example of it being used in
build-ratio-param
in the examples
namespace, which takes the beats
and cycles
cue
parameters chosen by a user, and divides them to create the ratio that
an oscillated parameter needs:
(params/build-param-formula Number #(/ %1 %2) beats-param cycles-param)
The build-param-formula
function takes the param-type
of the
parameter you want to create (in this case a Number
), a function
calc-fn
that will be called to calculate the parameter value when
needed (in this case, an anonymous function that simply divides its
first argument by its second), and then the list of other dynamic
parameters that will be evaluated and fed as input to calc-fn
. This
is a very compact way to perform calculations to combine or transform
other dynamic parameters.
If you want to perform geometric transformations on
Aim and Direction
parameters, there are some helper functions for that as well.
build-direction-transformer
takes an incoming direction parameter and a Java3D
Transform3D
object which will be used to transform it. Both can by dynamic
parameters, including keywords that will be looked up as show
variables. Similarly,
build-aim-transformer
applies a transformation parameter to an aim parameter.
Debugging Dynamic Parameters
Since dynamic parameters are such a source of flexibility, they can get complex quickly, especially when you are driving them from external systems via MIDI events. Here are a few tips on how you can check whether the parameter is doing what you expect, and how it is feeding into the effects you are creating with it.
Checking Variable Parameters
If you are using a show variable to hold values as the basis of your dynamic parameter, perhaps by mapping incoming MIDI events to it, can check the current value of the variable at any time like this:
(show/get-variable :key)
If you want to be informed more proactively whenever the show variable
value changes, you can register a
watch
function to be called whenever the variable changes. The following example
prints the new values of the variable named :key
each time it is changed.
(show/set-variable! :key 0)
; nothing special happens
(defn println-on-change
"Prints a variable every time it changes"
[key value]
(println key "set to" value))
(show/add-variable-set-fn! :key println-on-change)
(show/set-variable! :key 10)
; prints ":key set to 10"
Evaluating Other Parameters
For all the other kinds of dynamic parameters, there isn’t a place
where their value is stored; instead, it is calculated for a
particular point in time (and perhaps space). But you can ask the
parameter to evaluate itself by giving it the proper, context, in the
same way Afterglow itself does, using the
evaluate
function in the
IParam
protocol. All dynamic parameters implement this protocol. To call
evaluate
, you pass in the dynamic parameter, the show in which it is
running, and a metronome snapshot to identify the instant in time you
want to ask about. If you are testing a spatial parameter, you will
also want pass in the fixture head that you are asking about.
Otherwise, you can leave that last parameter nil
.
The graphterglow project includes a bunch of examples of doing this, and graphing the results. It is how the graphs of oscillators and step parameters in this documentation were created. Its build-test-snapshot and build-beat snapshot functions show how to create a snapshot for a certain number of millseconds or beats since the start of the metronome. Using them to evaluate a parameter looks like this:
(afterglow.effects.params/evaluate
my-param *show* (build-beat-snapshot (:metronome *show*) 5) nil)
That would determine the value of the dynamic parameter my-param
at
five beats past the start of the show.
If you are working on a tricky oscillated or step parameter, or any other sort of numeric dynamic parameter, getting it set up for graphing within graphterglow might help you get a visual insight for how it is behaving.
Digging Deeper
For more details, see the API documentation.