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

:type

Number

The type of value that is expected in the variable

:default

0

The value to assign the parameter if the variable is missing or has the wrong type

:transform-fn

none

If supplied, is called with the value of the show variable, and its return value is used as the value of the parameter

:frame-dynamic

true

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

:min

0

The smallest value that the oscillated parameter will be assigned

:max

255

The largest value that the oscillated parameter will be assigned

:metronome

none

If supplied, is used instead of the main show metronome

:frame-dynamic

true

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:

Default Step Parameter

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)
Step Parameter with 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)
Step Parameter with 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)
Step Parameter with 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)
Step Parameter with sine curve and 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)
Step Parameter with sine curve and 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)
Step Parameter with sine curve and 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)
Step Parameter with sine curve, fade fraction 0.2, interval ratio 1/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

:color

black

The base, starting color of this dynamic color

:r

0

Red brightness, from 0 to 255

:g

0

Green brightness, from 0 to 255

:b

0

Blue brightness, from 0 to 255

:h

0.0

Hue value, from 0.0 to 360.0

:s

0.0

Saturaion value, from 0.0 to 100.0

:l

0.0

Lightness value, from 0.0 to 100.0

:adjust-hue

0.0

Hue shift value, from -360.0 to 360.0

:adjust-saturation

0.0

Saturation shift value, from -100.0 to 100.0

:adjust-lightness

0.0

Lightness shift value, from -100.0 to 100.0

:frame-dynamic

true

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:

  1. The base color is established by the :color parameter.

  2. 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.

  3. 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.

  4. 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).

  5. 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.

  6. 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

:x

0

The amount the light should point towards audience’s right

:y

0

The amount the light should point up

:z

1

The amount the light should point towards the audience

:frame-dynamic

true

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

:x

0

How many meters along the X axis the target point is found

:y

0

How high up or down the Y axis is the target point

:z

2

How far towards or away from the audience is the target point

:frame-dynamic

true

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

:pan

0

How many degrees counter-clockwise should the light turn around the Y axis

:tilt

0

How many degrees counter-clockwise should the light turn around the X axis

:radians

false

Supply a true value with :radians if you would rather work in radians than degrees for your :pan and :tilt values.

:frame-dynamic

true

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

:min

n/a

If present, activates result scaling, and establishes the smallest value this dynamic parameter will hold.

:max

n/a

If present, activates result scaling, and establishes the largest value this dynamic parameter will hold.

:frame-dynamic

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.