Effects
Effects determine what a light or group of lights are doing at a given moment in time.
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.
For the common case of wanting to fade in an effect when you start it, and then fade it back out when you tell it to end, see Fading In and Out. |
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:
(show/set-cue!
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.
Fading In and Out
Sometimes you would like an open-ended effect to fade in when you
launch it, and then fade out when you ask it to end. To facilitate
this, Afterglow includes a special compound effect you can wrap around
any other effect to implement this behavior, building on the fade
mechanism described above. This is built using the
wrap-fade-in-out
function in the afterglow.effects
namespace.
This function takes a name for the wrapped effect, and the effect you
want to wrap with fade-in and fade-out behavior. By default it will
fade in and out over a beat, but you can change that by passing
keyword arguments :step-in
and/or :step-out
to configure the
Step Parameters that will be used
to manage the two fades. The values you pass with these arguments are
maps that will be merged onto {:fade-fraction 1.0}
and then passed to
afterglow.effects.params/build-step-param
to build the step functions that control the fade in and fade out process.
The most likely arguments you would want to use to adjust these fades are
:interval
and :interval-ratio
.
Here is an example of how, in the sample show, you could wrap a dimmer cue so that it fades in over two beats, and fades out over half a beat:
(show/set-cue!
7 0
(cues/cue :dimmer
(fn [var-map]
(fx/wrap-fade-in-out
"Fade Test"
(dimmer-effect
(params/bind-keyword-param (:level var-map 255) Number 255)
(show/fixtures-named :ws))
:step-in {:interval-ratio 2}
:step-out {:interval-ratio (/ 1 2)}))
:variables [{:key "level" :min 0 :max 255 :start 255}]))
You can omit the
:step-in
and:step-out
lines if the default of fading over a single beat is what you want.
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 Basic 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.