Effects

Overview

This section starts out by describing the basic effect types which act as building blocks for an Afterglow light show, then goes on to introduce the scenes and chases that you can use to group and sequence these building blocks into a show that evolves over time, driven by the show’s metronome.

When creating an effect, you assign one or more Fixtures to it, and it will generate control values for only those fixtures. The fixture channels that will be manipulated by the effect depend on the kind of effect it is, and are grouped into several primary categories. The simplest kinds of effects apply to a single channel per fixture, or perhaps per head, if the fixture has multiple independent light-emitting heads. A very common single-channel effect is the dimmer effect.

Many effects assign colors to lights, which often involves multiple channels. RGB fixtures will use three channels, one each for the red, green, and blue levels which make up the color. Some fixtures add other LED colors, such as amber, white, and even ultraviolet. Other fixtures simply use a rotating color wheel to pick from a fixed set of colors, using a single channel. Regardless of how the color is finally produced, at most stages of effect processing, Afterglow works in terms of the color itself, allowing you to blend, lighten, darken, shift hue, and the like so you can produce interesting looks, without regard to details of fixture implementation. It is only at the final rendering stage that the color is broken down into channel assignments.

Another important category of effects control the direction of moving-head fixtures. These work in terms of a desired 3D direction vector or target point with respect to the show frame of reference, and at the last stage of rendering, convert that to pan and tilt channel values, with reference to the angle at which the fixture has actually been hung.

You can also conveniently use features which are shared by a number of fixtures, or completely unique to a single fixture, through function effects, or drop right down to the level of individual DMX channel values with channel effects.

As mentioned above, there are a number of ways to combine these effects. If you have a group of effects that you want to start and stop at the same time, you can group them into a scene. To smoothly transition from one effect to another you can use a fade, and either side of the fade can be a scene. Or you can fade to or from a blank effect, or sequence a whole list of effects or scenes using a chase.

Conditional effects and variable effects are designed to make it easy for effects to influence and interact with each other by responding to and setting show variables. Finally, Afterglow includes some examples of how to build up special-purpose effects, which can be fun in themselves, and also serve as templates and inspiration for whole new kinds of effects you create on your own.

Basic Effect Types

Effects in afterglow are divided by the aspect of a light that they control. Not all fixtures have features which allow them to participate in all kinds of effects. Afterglow effects will simply ignore fixtures which are unable to reapond to them.

Dimmer Effects

Dimmer effects set the basic brightness level of a fixture. To create an effect that will set the dimmer level of a fixture, use:

(afterglow.effects.dimmer/dimmer-effect level fixtures)

This effect finds all the dimmer channels associated with the specified fixtures (there may be multiple, if the fixture has more than one light-emitting head), and assigns them a level. 0 is off, 255 is maximum brightness. Dimmer effects can work with either fully dedicated dimmer channels (in which case the channel itself has a :type of :dimmer, and the entire DMX range is used for dimming), or multipurpose channels in which a subset of the DMX range is assigned to a function of type :dimmer, and the channel :type is something else.

If htp? is true, uses highest-takes-precedence rules when replacing any earlier or lower-priority effects which attempted to set this particular dimmer in the current rendering cycle. In other words, if an earlier effect chose a higher value, let it win, otherwise use the value this effect wants.

The API documentation also identifies some optional keyword arguments you can pass. If :htp? passed with a false, value this effect will simply overwrite any previous assignments it finds for the dimmers it is controlling. Omitting :htp? is the same as passing it with a true value. And you can change the master that scales the dimmer value by passing one along with :master. If you do not, the effect will be tied directly to the show’s grand master.

If you want your dimmer cue to affect only one head of a fixture, you can pass it just that head, rather than the entire fixture.

For some fixtures, generally those with a single, white light source, the dimmer is the only thing that determines the amount of light output. For color-mixing fixtures like RGB LED lights, each color channel has its own separate brightness level, and then dimmer scales the actual output of the light, so the final brightness will be a combination of both. In other words, if the red channel is set to 50% brightness, and the dimmer is also set to 50% brightness, the final red output will be at 25% brightness.

In addition to passing in a literal number for the level parameter, you can pass a Dynamic Parameter whose value depends on a Metronome, the physical location or orientation of the head, or a show variable, perhaps bound to a MIDI control surface. It often makes sense to use oscillated parameters with dimmer cues.

If you have color effects running for a light and you aren’t seeing anything, make sure you have a dimmer effect setting the dimmer to some visible level. And if the fixture has a shutter (either physical or electronic), you may need an effect running to open that as well.

Virtual Dimmers

Although most RGB color-mixing fixtures have a dimmer channel which is separate from the color component intensity channels, we have encountered a few that lack this feature: they have only a red, green, and blue channel, with no overall dimmer. In order to accommodate fixtures like this, and enable them to participate in dimmer effects for more flexible show control, Afterglow now can provide virtual dimmers for them. To use this feature, pass the optional argument :add-virtual-dimmers? with a true value to dimmer-effect.

As explained below, when using virtual dimmers you must also turn off highest-takes-precedence dimmer mixing by passing false with the :htp? optional keyword argument, and add your dimmer effect to the show with a higher priority than any color effects running on the fixtures that lack real dimmers, so it can run after them and manipulate their results.

Since virtual dimmers do not have an actual separate dimmer channel to work with, they are actually color effects which work by modifying the result of whatever other color effect you have established for the dimmer-lacking fixtures. When you tell dimmer-effect that add-virtual-dimmers? is true, it will scan the list of fixtures whose dimmers it is supposed to control, and in addition to creating ordinary dimmer effects for fixures with dimmers, it will create special color effects for any fixtures that can mix RGB colors but lack dimmers. These color effects will act as virtual dimmers: they will look at the color value that has been established for the fixture, and darken it as needed in order to reflect the dimmer level of the dimmer effect that created them (scaled by the dimmer master chain, just like regular dimmer effects). This dimmed version of the color will replace the one that came from the other effects, and the visible result will be the same as if the fixtures had dimmer channels.

In order for the virtual dimmer effect to be able to modify the other color effects, it needs to run after them. So you need to be sure when you are adding virtual dimmer effects to the show to assign them a higher priority value than the color effects they will be working with.

Because the virtual dimmer color adjustment is a destructive operation, and the color channels have no direct access to the dimmer level information once it is complete, virtual dimmers cannot be combined with each other in a “highest takes precedence” style. So when you need to use virtual dimmers, you must set :htp? mode to false in dimmer-effect, or the function will throw an exception.

You should also not try to run more than one dimmer effect with virtual dimmers on the same fixtures at the same time, because each one will darken them, giving you different results than actual dimmers do.

Even with these restrictions, virtual dimmers can address most of the limitations of fixtures that lack actual dimmer channels, and allow them to participate as nearly first-class citizens of Afterglow’s dimmer effects.

Color Effects

One of the most common basic effects you will be using to create the looks you want is the color effect. To create an effect that will assign a color to a fixture (which will assign colors to all of the fixture’s heads), or a single head of a fixture, pass the fixture or head to color-effect:

(afterglow.effects.color/color-effect name color fixtures)

The name parameter is intended to help identify the purpose of the effect, and shows up when examining the created effect. Put something descriptive in there, or use a helper function like afterglow.examples/global-color-effect which builds the effect for you, figuring out a reasonable name in many cases.

The color parameter is where you specify the color to assign to the lights. It should be a color object returned by one of the factories in the jolby/colors library, as described in Working with Color.

In addition to passing in a color for the color parameter, you can pass a Dynamic Parameter whose value depends on a Metronome, the physical location or orientation of the head, or a show variable, perhaps bound to a MIDI control surface. The flexibility offered by dynamic color parameters is huge, especially when combined with oscillated parameters. Learning how to effectively leverage these in combination with each other will enable you to create most of the basic lighting looks you need.

The following code creates a effect that assigns a pure red color to all fixtures with RGB channels, then adds it to the current show under the keyword :color:

(show/add-effect! :color (afterglow.effects.color/color-effect
                          "Plain red" (create-color "red") (show/all-fixtures)))
Remember that if aren’t seeing anything when you have assigned color effects to a fixture to make sure you also have a dimmer effect setting that fixture’s dimmer to some visible level. And if the fixture has a shutter (either physical or electronic), you may need an effect running to open that as well.

You can also assign colors to lights that use color wheels instead of RGB mixing by passing a true value with the optional keyword argument :include-color-wheels? to color-effect. Afterglow will try to find a color wheel position which is close enough to the desired color, and if one can be found, tell the light to use it.

Multiple Colors

You can assign different color effects to different sets of fixtures even just using simple single-color effects, by combining them into a scene, which is explained more fully below, but worth mentioning now. Here is an example of how to assign a red color to odd fixtures and blue to even fixtures (assuming you have named the fixtures even-number and odd-number):

(show/add-effect! :color (afterglow.effects/scene "Different colors"
  (afterglow.effects.color/color-effect
    "Plain red" (create-color "red") (show/fixtures-named "odd"))
  (afterglow.effects.color/color-effect
    "Plain Blue" (create-color "blue") (show/fixtures-named "even"))))

The Cues documentation extends this example to show how to wrap this scene into a cue, for easy control by a light show operator.

There are many other ways to achieve multi-colored effects, ranging from Spatial Effects up to writing your own custom Complex Effects. You can also group fixtures any way you want, independently of how you name them, by storing sets of them in variables and passing those sets, or combinations of those sets created using Clojure’s rich set-manipulation API, to the effect-creation functions.

Direction Effects

Moving-head fixtures can create particularly exciting and dynamic shows. To create an effect that will tell a fixture or head what direction it should be pointing, pass the fixture or head to:

(afterglow.effects.movement/direction-effect name direction fixtures)

The name parameter is intended to help identify the purpose of the effect, and shows up when examining the created effect.

The direction parameter is where you specify the direction the lights should be pointing. It is a javax.vector.Vector3d pointing in the direction the lights should face, with respect to the show’s frame of reference. An easy way to create one is to call afterglow.effects.params/build-direction-param or afterglow.effects.params/build-direction-param-from-pan-tilt. These can create static vectors for you, but can also create Dynamic Parameters whose value depends on a Metronome, the physical location or orientation of the head, or a show variable, perhaps bound to a MIDI control surface. Building dynamic direction parameters with oscillated parameters can create fascinating motions.

If a group of fixtures is assigned the same direction effect, they will all face the same direction. If they are assigned the same aim effect (below), they will all face slightly different directions in order to aim at the same point in space.

Because of the fact that the direction vector must be translated into pan and tilt angles before sending it to control the light, fades between directions might not always work the way you expect them to. This is especially true if the directions you are fading between are exact opposites of each other: In that case, the angle does not change at all during the fade until the midpoint, when it reaches the center of the light, and the light instantly flips around to face the opposite direction for the rest of the fade. You can also run into issues where one of the directions you are fading is close to a geometric singularity (when one of the angles gets near 90°), at that point the other direction will suddenly dominate, and you can see unexpected jiggling or changes in direction. For such cases you may be better off using lower-level pan/tilt effects, which operate closer to the way the lights themselves do.

Aim Effects

These are very similar to direction effects, except they tell each fixture to aim at a particular point in space, such as an object or person in front of the lighting rig, or perhaps another fixture. To create an effect that will tell a fixture or head what point it should be aiming at, pass the fixture or head to:

(afterglow.effects.movement/aim-effect name target-point fixtures)

The name parameter is intended to help identify the purpose of the effect, and shows up when examining the created effect.

The target-point parameter is where you specify the point at which the lights should be aiming. It is a javax.vector.Point3d identifying a point within the show’s frame of reference. An easy way to create one is to call afterglow.effects.params/build-aim-param. This can create static points for you, but can also create Dynamic Parameters whose value depends on a Metronome, the physical location or orientation of the head, or a show variable, perhaps bound to a MIDI control surface. Using a tablet with an OSC or midi interface that lets you drag an aiming point around a map of the stage is one fun possibility.

If a group of fixtures is assigned the same direction effect, they will all face the same direction. If they are assigned the same aim effect, they will all face slightly different directions in order to aim at the same point in space.

Pan/Tilt Effects

These are essentially the same as direction effects, except they use a pan and tilt angle to tell the fixtures which way to face, so they are closer to the way the lights naturally work, will be more familiar to light show designers, and can behave more smoothly and predictably when fading into each other. To create an effect that will tell a fixture or head what direction it should be pointing via pan and tilt angles, pass the fixture or head to:

(afterglow.effects.movement/pan-tilt-effect name pan-tilt fixtures)

The name parameter is intended to help identify the purpose of the effect, and shows up when examining the created effect.

The pan-tilt parameter is where you specify the angles in which the lights should be aiming. It is a javax.vector.Vector2d whose x component contains the pan angle, and whose y component contains the tilt angle. These angles tell the fixture how far, in radians, it should rotate away from pointing straight out at the audience (along the z axis of the show’s frame of reference). An easy way to create the pan-tilt vector is to call afterglow.effects.params/build-pan-tilt-param. This function also allows you to work in degrees rather than radians, if that is more convenient. It can create static angle vectors for you, but can also create Dynamic Parameters whose value depends on a Metronome, the physical location or orientation of the head, or a show variable, perhaps bound to a MIDI control surface.

Because when you fade between pan-tilt effects, the angles always change smoothly, and correspond to the actual movements of the lights, they can be easier building blocks for natural-looking movement effects when you aren’t trying to track particular points in space.

If a group of fixtures is assigned the same pan-tilt or direction effect, they will all face the same direction. If they are assigned the same aim effect, they will all face slightly different directions in order to aim at the same point in space.

Function Effects

Fixtures have a wide variety of different capabilities, often more than would be reasonable to assign a separate DMX channel for each, especially when it does not make sense to activate or control some at the same time. Afterglow can be told about these in the fixture definition, and you can control them using function effects, by specifying the name of the function you want to activate, and a percentage (a value between 0 and 100) by which you want it activated. (The percentage will be translated to the corresponding value within that function’s valid DMX range that Afterglow should send).

For example, many fixtures have a strobe function, which causes them to flash off and on at a particular speed. The following line shows how to cause them all to strobe at their fastest speed:

(show/add-effect! :strobe (afterglow.effects.channel/function-effect
  "Fastest strobe" :strobe 100 (show/all-fixtures)))

With this effect active, any fixture with a :strobe function range will be sent the highest value defined for that range, on the channel on which the function exists, causing it to strobe rapidly. Fixtures which lack such a function will be unaffected.

Function effects can be very specific to individual fixtures. For example, the Blizzard Torrent F3 has a pair of gobo wheels; one of them has a gobo that projects something that looks like a fat atom with electrons orbiting it. This projection can be selected, and caused to jiggle back and forth at the mid-range of possible shake speeds, by adding the following effect:

(show/add-effect! :gobo-fixed
  (afterglow.effects.channel/function-effect "Brownian motion?"
    :gobo-fixed-atom-shake 50 (show/fixtures-named "torrent")))

Depending on how far away the projection is landing, it may be very blurry; focus can be adjusted like so:

(show/add-effect! :focus
  (afterglow.effects.channel/function-effect
    "focus" :focus 95.5 (show/fixtures-named "torrent")))

The functions available for a fixture, their names, channels, and ranges, are specified by the fixture definition, so reading over those can be helpful. (And carefully crafting and testing them is important when defining a new fixture.) Trying to maintain consistency in function naming is valuable in allowing functions to be conveniently applied to groups of different fixtures.

Functions which do not vary in their effect for different DMX values within the legal range are described as :range :fixed in the fixture definition; this is currently only used for displaying the interpretation of a fixture setting, you still need to provide a percentage within the range when setting up the function effect.

Fixture definitions can also supply a scaling function for a function specification, which maps input values to the final percentage within the DMX range. This is helpful, for example, to allow strobe settings to be interpreted as approximate Hz values, so fixtures from different manufacturers can be asked to strobe at roughly the same rate for the same function setting. You can view the source of the Blizzard fixture definitions for examples of how this is done, passing the minimum and maximum Hz strobe rates of the actual fixture to create a partial implementation of afterglow.effects.channel/function-value-scaler which is passed the value that the effect is trying to establish, and converts it to a position in that fixture’s range which attempts to approximate that strobing rate.

Channel Effects

When you just want to send a specific number to a particular DMX channel, you can drop right down to the bottom level with channel effects. For example, to pin the dimmer channel of a group of fixtures to 55, regardless of the setting of the show’s master chain, you could do something like this:

(show/add-effect! :blade-dimmers
  (afterglow.effects.channel/channel-effect "Blade dimmers" 55
    (afterglow.channels/extract-channels
      (show/fixtures-named :blade) #(= (:type %) :dimmer))))

Or to look at what actual pan values do to a Torrent, without fancy geometric transformations, as you set values into the show variable named :pan:

(show/add-effect! :pan-torrent
  (afterglow.effects.channel/channel-effect
    "Pan Torrent" (params/build-variable-param :pan)
    (afterglow.channels/extract-channels
      (show/fixtures-named :torrent) #(= (:type %) :pan))))

You will most likely be wanting to do this sort of thing for channel types which Afterglow does not yet have a more sophisticated understanding, and then perhaps you will end up creating a whole new category of effects as your experimentation progresses.

Compound Effects

The most straightforward way to create interesting shows is to combine multiple simple effects in different ways. Compound effects are tools which enable that.

Scenes

The simplest way to build a compound effect is to combine a group of effects into one which can be started and stopped as a unit. That is the purpose of the scene function in the afterglow.effects namespace. It takes a name for the scene to be created, followed by one or more effects to be grouped, and returns an effect which combines them all under that name:

(show/add-effect! :color
  (afterglow.effects/scene "Blue Sparks"
    (afterglow.examples/global-color-effect :blue)
    (fun/sparkle (show/all-fixtures) :chance 0.07 :fade-time 500)))

Assuming you are running the sample show and have the dimmers up, you’ll see all the lights turn blue, and a random pattern of white sparkles twinkling across them. Ending the scene effect will end both underlying effects in a coordinated fashion (the blue color effect will linger as the last sparkles fade out).

Blank Effects

A blank effect does nothing at all. Although this might not immediately seem useful, assigning a blank effect to one side or the other of a fade (below) lets you fade an effect in or out, from or to nothing. In such cases the fade also takes care that as it fades towards the blank effect, whatever effects were being replaced by the fade are restored.

To create a blank effect, simply call the blank function in the afterglow.effects namespace.

You might also want to create a blank effect as part of a cue whose purpose is simply to provide a way to adjust a show variable. The Colors and Cues discussion provides an example of doing just that.

Fades

A fade effect lets you smoothly transition from one effect to another, blending a weighted combination of each. The fade function in the afterglow.effects namespace supports this. It takes a name for the fade to be created, followed by from-effect and to-effect, the two effects to be faded between, and a phase parameter which controls how much of each effect is seen. It returns the blended effect.

When the value of phase is 0 (or less), the fade acts as if it is simply from-effect. When phase is 1 (or more), the fade behaves identical to to-effect. When phase falls somewhere between 0 and 1, a corresponding linear blend between from-effect and to-effect is created. At the value 0.5, each effect contributes the same amount.

Either or both of the effects being faded between can be a scene, which groups many other effects, or one can be a blank effect, which will simply fade the other effect in or out of existence (allowing any earlier or lower-priority effects to show through). When fading between two non-blank effects, if they include different groups of fixtures (or affect different aspects of the fixtures they do include), the same notion of “seeing what is underneath” the fade applies, as the side which is controlling a particular fixture or feature is faded out.

The phase parameter can (and usually will) be a dynamic parameter, probably a variable parameter or oscillated parameter, so the fade will take place over time, or under the control of an operator using a control surface.

Here is an example of a very simple fade cue from the sample show:

(ct/set-cue! (:cue-grid *show*) 4 7
             (cues/cue :color-fade
                       (fn [var-map]
                           (fx/fade "Color Fade"
                                    (global-color-effect :red :include-color-wheels? true)
                                    (global-color-effect :green :include-color-wheels? true)
                                    (params/bind-keyword-param (:phase var-map 0) Number 0)))
                       :variables [{:key "phase" :min 0.0 :max 1.0 :start 0.0 :name "Fade"}]))

This fades all the lights from red to green as the cue’s encoder is turned. Switching either color effect to (blank) would insted fade to or from whatever color the fixtures were otherwise displaying at the time.

Chases

Chase effects allow you to sequence a series of effects one after another, with optional fades between them. They are built using the chase function in the afterglow.effects namespace. Of course each effect within the chase can itself be a scene, which groups many other effects, or a blank effect, which will simply fade the chase temporarily out of existence (allowing any earlier or lower-priority effects to show through). When fading between two non-blank effects, if they include different groups of fixtures (or affect different aspects of the fixtures they do include), the same notion of “seeing what is underneath” the fade applies, as the side which is controlling a particular fixture or feature is faded out.

In addition to the list of effects which make up the chase, a position parameter is used to create it. When the effect is rendered, the current value of this parameter is an index into the effects that make up the chase, and it controls which one is currently visible. When position is 1, the first effect in effects is active; 2 causes the second to be seen, and so on. Non-integer values are how fades are accomplished, they result in a linear blend between the corresponding effects. In order to make the chase evolve over time, position needs to be a dynamic variable parameter, and Step Parameters, created by the function afterglow.effects.params/build-step-param, are designed specifically to work with chases.

With no other arguments, the chase will end when position has a value less than zero, or greater than the number of elements in effects plus one. Values between 0 and 1 fade into the first effect from nothing, and as the value grows above the number of entries in effects, it begins to fade out the final effect.

A chase can be made open-ended by supplying a value with the optional keyword argument :beyond. The default value, :blank, causes the behavior described in the previous paragraph. If :beyond is supplied with the value :loop, the chase will act as if the effects list contained an infinite number of copies of itself. So when position grows past the final index, the last effect in the list fades back into the first entry. Similarly, values of position below 1 fade back to the end of the list. In this configuration, the chase will only end when either all of the underlying effects contained within the effects list have ended on their own, or position resolves to nil, which always ends a chase immediately.

Another way to create an open-ended chase is to pass :beyond with the value :bounce. This acts like :loop, except that whenever one end of the list of effects is reached, the chase changes direction and moves back through the list from that point. In other words, if position keeps growing steadily in value, and there are three effects in effects, with a :beyond value of :loop you will see them in the order 1 → 2 → 3 → 1 → 2 → 3 → 1… while a value of :bounce would give you 1 → 2 → 3 → 2 → 1 → 2 → 3 → 2….

Conditional Effects

The conditional-effect function in the afterglow.effects namespace wraps another effect, allowing it to run only when the value of some dynamic parameter (most likely a variable parameter or oscillated parameter) is not zero. afterglow.shows.sallie/global-color-effect shows an example of using it within a scene to optionally have the color effect apply to a laser show running simultaneously with the light show, controlled by the show variable :also-color-laser. This variable gets set when the “Also color laser” cue is running, by means of a Variable Effect, described in the next section.

(ns afterglow.shows.sallie
;; ...
  (:require [afterglow.effects :as fx]
;; ...
)
;; ...
(fx/scene (str "Color: " desc)
          (color-effect (str "Color: " desc) c lights)
          (fx/conditional-effect "Color Laser?" (params/build-variable-param :also-color-laser)
                                 (beyond/laser-color-effect laser-show c))))

Variable Effects

The variable-effect function in the afterglow.effects.show-variable namespace creates an effect which does not set any DMX values. Instead, it makes use of the rendering loop extension mechanism to set a show variable while the effect is active. This dovetails very nicely with Conditional Effects, described above.

You can see an example of how to use variable effects in afterglow.shows.sallie/use-sallie-show, which creates a binding to the show variables using afterglow.effects.show-variable/create-for-show. Then afterglow.shows.sallie/make-cues uses that var-binder to create a :color-laser cue which sets the show variable :also-color-laser while it runs:

(ns afterglow.shows.sallie
;; ...
  (:require [afterglow.effects.show-variable :as var-fx]
;; ...
)
;; ...
(reset! var-binder (var-fx/create-for-show *show*))
;; ...
(ct/set-cue! (:cue-grid *show*) 5 7
             (cues/cue :color-laser
                       (fn [_] (var-fx/variable-effect @var-binder :also-color-laser 1))
                       :color :red :short-name "Also color laser"))

This variable setting causes the Conditional Effects in scenes created by global-color-effect (as described in the preceding section) to also send commands to the laser show.

Complex Effects

These are effects which build on more than one of the capabilities listed above to create an interesting or fun effect. They represent examples of how Afterglow can be used to create new things, and we hope that people will contribute their own effects for inclusion in future releases.

Color Cycle Chases

This family of related effects are an excellent illustration of why Afterglow was created, which was to enable the concise expression and implementation of effects like them. They leverage many of the building blocks within Afterglow, and provide a framework to combine them in flexible ways using functional composition to acheive a variety of different looks that change in space at appropriate musical times, with very little code required in each. They are useful in themselves, and as examples of how to write similar effects.

The Iris Out color cycle chase changes the color of a group of fixtures to a different color for each bar of a phrase of music. During the down beat of each new bar, the color spreads over the participating fixtures starting at their geometric center in the x-y plane of show space, and spreading in an expanding circle until reaching the furthest heads at the end of the down beat.

(show/add-effect! :color
  (afterglow.effects.fun/iris-out-color-cycle-chase (show/all-fixtures)))

If you look at the source code (which you can always get to by following the “view source” link at the bottom of the API documentation, or typing (source afterglow.effects.fun/iris-out-color-cycle-chase) in a REPL or the web console), you will see that it is only a few lines, once you get past the documentation and parameters, most of which are given default values to pass along to color-cycle-chase, which is used to actually implement the chase.

Those parameters can be used to change the set of colors in the cycle, as well as control when the color changes, and when and how quickly the transition occurs. The documentation for color-cycle-chase explains how.

The body of iris-out-color-cycle-chase simply sets up the measure function which causes the iris-out effect to behave as described, measuring a circular distance in the x-y plane (ignoring the z axis) from the center of the fixtures that have been assigned to participate in the effect. This is why it is easy to set up a family of similar effects which create different spatial transitions for the color cycle chase.

For example, Wipe Right, which transitions the lights from left to right, ignoring both the y and z axes. The work of both of these chases is simplified with the help of afterglow.transform/build-distance-measure, a function for constructing distance measure functions for use in effects like this, and afterglow.transform/calculate-bounds, which calculates a bounding box and center for a group of fixtures and the heads which make them up.

(show/add-effect! :color
  (afterglow.effects.fun/wipe-right-color-cycle-chase (show/all-fixtures)))

Sparkle

Creates a random sparkling effect like a particle generator over the supplied RGB fixture heads. See the API documentation for details.

(show/add-effect! :sparkle
  (afterglow.effects.fun/sparkle (show/all-fixtures)))

You can also create a similar effect with fixtures that have only dimmer channels, rather than RGB capabilities.

(show/add-effect! :sparkle
  (afterglow.effects.fun/dimmer-sparkle (show/all-fixtures)))

And if you want to run a sparkle effect on both kinds of fixtures at the same time, you can combine these two effects with a Scene.

(show/add-effect! :sparkle
  (afterglow.effects/scene "Sparkle all"
    (afterglow.effects.fun/sparkle (show/all-fixtures))
    (afterglow.effects.fun/dimmer-sparkle (show/all-fixtures))))

Strobe

A flexible strobe effect designed for intuitive tweaking via pressure-sensitive controllers like the Ableton Push. See the API documentation for details.

(show/add-effect! :strobe-all
  (afterglow.effects.fun/strobe "Strobe All" (show/all-fixtures) 50))

Metronome

The Metronome cue is a way to check the synchronization of the show metronome with your DJ software or mixer if you don’t have an Ableton Push or an easy way to pull up the web interface, and is mostly a nice example of how to write a cue that is driven by a metronome. It was one of the first clearly metronome-driven effects written, and was extremely useful when developing the metronome sync facilities (especially since at the time there was no web or Ableton Push interface, with their metronome monitoring and adjustment sections). Today it is less interesting, especially compared to the color cycle chases described above.

(show/add-effect! :color
  (afterglow.effects.fun/metronome-effect (show/all-fixtures)))

Creates an effect which flashes the heads of the supplied fixtures one color on the down beat and another color on the other beats of the show metronome. The default down beat color is a lightened red, and the other beat color is a darkened yellow; these can be overridden by optional keyword parameters. See the API documentation for details.

The Effect Lifecycle

When an effect is added to a show via (show/add-effect! :effect-key effect) it immediately replaces any other effect which had been previously added with the same keyword. The former effect does not get a chance to gracefully finish its effects, it is simply gone. The new effect is added to the rendering loop in a position determined by the priority value, if any, specified after the optional :priority keyword argument. If no priority argument is supplied, a priority of zero is used. The new effect is added after any other existing effects of the same (or lower) priority, but before any existing effects with higher priority. Since later effects get a chance to override earlier effects, this means that higher-priority effects, and effects added later, win.

All effects implement the afterglow.effects/IEffect protocol. As each frame of lighting control values is rendered, a snapshot is created from the show metronome, so every effect shares the same notion of the point in time at which effects are being rendered. The priority-ordered list of effects is traversed, and each effect’s (still-active? [this show snapshot]) function is invoked to determine if the effect has ended at this point. If this returns true, the effect is removed from the list of active effects, and is finished. Limited-time effects can use this mechanism to tell the show when they finish. Ongoing effects will simply always return true, or if they want to end gracefully, will return true until they have been asked to end, and their graceful ending has completed.

Assuming the effect has not reported completion, its (generate [this show snapshot]) function will be called, as described in the rendering loop section, to create the effect it represents at this point in time.

At some point, the show operator may indicate a desire for the effect to end, by calling (show/end-effect! :effect-key force). If force is true, the specified effect will simply be removed from the list of active effects. If force is omitted or false, the effect is asked to end gracefully by calling its (end [this show snapshot]) function. If the effect is ready to end right away, it can return true, and will be removed at that point. Otherwise, if it wants to take a little while to animate an ending effect, it should set an internal flag so it knows it is ending and return false, and at some point in the not-so-distant future, conclude its ending and return false from still-active?.

As implied by the preceding paragraph, your effect cannot rely on its end function ever being called. If the effect is ended forcibly, if another effect is added under the same keyword, or if it is taking part in a fade, at some point it will simply be discarded. It must therefore not retain any resources that will not be reclaimed by simple garbage collection.

If end-effect! is called a second time for an effect which was already asked to end, even if force is false, it will be removed forcibly at that point.

Effect Examples

Here are a few ways in which effects can be used and combined.

These examples assume you are in a Clojure REPL with Afterglow loaded, in the namespace afterglow.examples. This is the default namespace you get if you check out the project and run lein repl.

Oscillator Effects

Oscillators in Afterglow are a flexible way of turning the timing information tracked by metronomes into waveforms that can be used to make lights do interesting things. They can be related to the beats or bars of the metronome, or multiples or fractions thereof, and can be sawtooth, triangle, square, or sine waves. Wikipedia has a nice introduction to these waveforms. The namespace afterglow.effects.oscillator has functions for creating lots of variations on them.

Here is one way to create a basic oscillated hue effect which cycles through all colors over one bar of the show metronome:

(def hue-param (oscillators/build-oscillated-param
                 (oscillators/sawtooth :interval :bar) :max 360))
(show/add-effect! :color (global-color-effect
   (params/build-color-param :s 100 :l 50 :h hue-param)))
Remember that if you aren’t seeing anything when after assigning color effects to a fixture to make sure you also have a dimmer effect setting that fixture’s dimmer to some visible level. And if the fixture has a shutter (either physical or electronic), you may need an effect running to open that as well.

We can set up separate metronomes as show variables, so that effect timing can be separate from the main show, which is intended to track the beat of the music. Here we will create a metronome running at 5 beats per minute in a show variable we will call timer.

    (show/set-variable! :timer (metronome 50))

Then we can build an oscillated hue parameter based on that timer, for a nice, gradual color fade. We will use a sawtooth wave since it smoothly goes from its minimum to its maximum value. Zero is the default minimum, which is perfect, since it is the lowest hue value. We will tell the oscillated parameter to range from that to a maximum of 360, the largest hue. Since hues form a circle, we will fade smoothly around the circle for each oscillation, with no jarring transition from one bar to the next:

(show/set-variable! :hue-param
  (oscillators/build-oscillated-param (oscillators/sawtooth :interval :bar)
    :metronome :timer :max 360))

Notice the use of the keyword :timer to tell build-oscillated-param to use the show variable with that name for its :metronome keyword parameter. We can do the same thing when building our color effect to use this oscillated hue parameter variable:

(show/add-effect! :color (global-color-effect
  (params/build-color-param :s 100 :l 50 :h :hue-param)))

We can change the speed of the fade by changing the BPM of the metronome stored in the show variable:

(metro-bpm (show/get-variable :timer) 500)

Suddenly it is crazy fast!

(metro-bpm (show/get-variable :timer) 5)

Back to a sedate fade.

Spatial Effects

Rather than spreading the rainbow out in time, how about if we spread it physically across the lights in the show, in the form of a rainbow gradient along the X axis?

(def hue-gradient (params/build-spatial-param (show/all-fixtures)
  (fn [head] (- (:x head) (:min-x @(:dimensions *show*)))) :end 360))
(show/add-effect! :color (global-color-effect
  (params/build-color-param :s 100 :l 50 :h hue-gradient)
                            :include-color-wheels true))
Since this cue is not constantly changing over time, it makes sense to allow fixtures that use color wheels to participate.

That’s pretty! But now that we have both of these interesting concepts, oscillators and spatial gradients, wouldn’t it be nice if we could combine them? Oh, but we can!

(def adjust-param
  (oscillators/build-oscillated-param (oscillators/sawtooth :interval :bar) :max 360))
(show/add-effect! :color (global-color-effect
  (params/build-color-param :s 100 :l 50 :h hue-gradient
                            :adjust-hue adjust-param)))
Now the rainbow drifts across the whole lighting rig. We left out color wheels this time, since the color is continually shifting.

The Effect Types section goes into more detail about how these effects work.

Looking at the source code of the complex effects is a great way to learn about how to create effects, and to get ideas for ways to vary or build on them.

Layering Effects

Rather than building separate effects for every combination of ideas, you can get much more power by building effects that build on or modify each other, which you can then compose in different ways. The most straightforward way of doing this is by combining effects that work on different facets of the lights, such as when you choose a dimmer oscillator, to make them pulse in a particular way with the beat, along with a color effect, and perhaps an aim or direction chase. Varying these effects can give you quite a palette of looks.

The Afterglow rendering loop is designed to let you be even more flexible than that, though: you can combine multiple effects which work on the same channels of the same fixtures, because of the way that later (and higher priority) effects can see what earlier effects have done, and modify the results.

The transform-colors effect is an example of how easy and flexible this can be. (As always with the API documentation, you can click on the viw source button to see the actual implementation of the function.) This effect uses its own variable parameter to adjust the saturation of any color being sent to the fixtures it is assigned. (If there isn’t currently a color being assigned to those fixtures, it does nothing.) Calling it with no arguments uses a default transformation and oscillated parameter which causes the saturation of the color to start each beat fully saturated, and to fade to gray by the end of the beat. This was inspired by the rainbow fade effect which was initially created while experimenting with afterglow-max, but this generalization can be combined with any other color effect.

The transform-colors function itself does all the work of creating assigners that will watch for colors being sent to the fixtures it is supposed to affect, and whenever appropriate, transforming them. The transformation itself is separated into another function, which can be passed in as an argument to achieve a totally different kind of transformation. The default transformation if none is specified is created by calling build-saturation-transformation with no arguments. The source of this function shows how easy it is to write a transformation given the support provided by the Rendering Loop and transform-colors.

(defn build-saturation-transformation
  "Creates a color transformation for use with [[transform-colors]]
  which changes the saturation based on a variable parameter. If no
  parameter is supplied, the default is to use an oscillated parameter
  based on [[sawtooth]] with `:down?` set to `true` so the color
  is fully saturated at the start of the beat, and fully desaturated
  by the end. A different pattern can be created by supplying a
  different parameter with the `:param` optional keyword argument."
  {:doc/format :markdown}
  [& {:keys [param] :or {param (oscillators/build-oscillated-param (osc/sawtooth :down? true)
                                                                   :max 100)}}]
  (fn [color show snapshot head]
    (let [saturation (colors/clamp-percent-float
                      (params/resolve-param param show snapshot head))]
      (colors/create-color {:h (colors/hue color) :s saturation :l (colors/lightness color)}))))

This particular function takes an optional variable parameter to control what the current saturation should be (if you don’t provide one, it creates an oscillated parameter which implements the desaturate-over-each-beat behavior described above:

(oscillators/build-oscillated-param (oscillators/sawtooth :down? true) :max 100)

The downwards-direction sawtooth wave from 100 to 0 each beat causes the saturation pattern described; changing to a different wave form, or something which oscillates over a bar or phrase or fraction thereof, or with different :min and :max values would achieve a different effect.

The function returned by build-saturation-transformation is called by transform-colors when Afterglow is calculating a frame of DMX data to send to the lights, whenver one of the lights that the transform-colors effect has been applied to is being sent a color value. The function is called with the color that has so far been assigned to the light (in color), and the current show, metronome snapshot representing the current instant in musical time (and which can be used with an oscillated variable parameter as seen here to generate smoothly changing, rhythmically-driven values), and the light head this is being sent to (which can be used to perform spatial calculations as described above). The function returns a new color to replace the former assignment (or it could return nil to suppress coloring the light entirely).

Having all this information at hand, and the flexible power of oscillators and dynamic, oscillated, and spatial variable parameters, makes it possible to write straightforward, concise transformation functions like this one.

And of course you can change things other than saturation; take a look at the source and try writing your own transformation functions which do different things. When you come up with exciting looks, please contribute them back to Afterglow!

Remember that when you create a cue for an effect like transform-color, you want it to run after the other effects that it is going to transform, so give it a high effect priority. Here is how the sample show configures it:

(ct/set-cue! (:cue-grid *show*) 2 7
             (cues/cue :transform-colors (fn [_] (color-fx/transform-colors
                                                  (show/all-fixtures)))
                       :priority 1000))

And here is a complete, concrete example of how you can try out transform-colors from the REPL:

(show/add-effect! :color
  (afterglow.effects/scene "Blue Sparks"
    (global-color-effect :blue)
    (color-fx/transform-colors (show/all-fixtures) :priority 1000)))

This assigns a blue color to all the lights, and pulses them to white once per beat.

License

Deep Symmetry logo Copyright © 2015–2019 Deep Symmetry, LLC

Distributed under the Eclipse Public License 1.0, the same as Clojure. By using this software in any fashion, you are agreeing to be bound by the terms of this license. You must not remove this notice, or any other, from this software. A copy of the license can be found in LICENSE within this project.