Combining channels
When working with Punctual, since many functions and operators have a combinatorial nature, expressions often result in signals with a high number of channels. While this can be desirable in certain cases, it is often not ideal. More channels can make computations more challenging, potentially slowing down or even freezing your environment. It’s relatively common to encounter the need to exit and reenter an ensemble in Estuary due to overly complex patterns, often associated with a high channel count. Additionally, when mixing channels into add
or blend
, the default color treatment may not always align with your intended result.
Fortunately, there are functions available for combining or mixing different channels, allowing you to reduce complexity when needed.
mono
mono
: this is one of the simplest but most useful functions to combine channels. It simply takes a signal and combine all channels into one. Very useful when we want multiple shapes in our pattern, but don’t need to have them split in different channels.
Three white circle at different heights:
y << [(-0.5),0,0.5];
mono $ circle [0, y] 0.1 >> add;
Some rotating lines intersecting at the center of the screen:
x << [0.5,0.4..(-0.5)];
y << (x+saw 0.01);
mono $ iline 0 (zip x y) 0.002 >> add;
In order to mix channels, mono
simply adds them. This is acceptable when each channel represents a shape, like in the previous examples, but can be a problem in other situations. For example, mono [0.5, 0.5] >> add;
evaluates to 1 >> add;
, resulting in a completely white screen.
blend
blend
is intended to mix channels into a group of four, to be used with>> blend
. For every two groups of 4 channels, they are mixed by using the alpha channel of the second group to interpolate between the two.
For example:
c1 << [1, 0, 0.7, 1];
c2 << [0.5, 1, 0.3, 0.2];
blend $ c1++c2 >> blend;
The resulting color is [1*0.8+0.5*0.2, 0*0.8+1*0.2, 0.7*0.8+0.3*0.2, 1*0.8+0.2*0.2] = [0.9, 0.2, 0.62, 0.84]
.
rep
rep
takes an integern
and a graphg
as arguments. It replicates the channels ing
n
times. So, if for exampleg
has 3 channels the result of applyingrep 2
will have 6.
NOTE: This function is currently undocumented.
rep 3 (unipolar $ osc 0.1) >> add;
This is equivalent to:
o << unipolar $ osc 0.1;
[o,o,o] >> add;
unrep
unrep
takes an integern
and a graphg
as arguments. It mixes everyn
consecutive channels ing
into 1. So, if for exampleg
has 12 channels, the result of applyingunrep 3
will have 4.
NOTE: This function is currently undocumented.
l << hline [0.9,0.8..(-0.9)] 0.001;
unrep 4 l >> add;
Here, l
has a total of 19 channels. With unrep
they are grouped 4 by 4, with the exception of the last group that only joins 3 channels.
It’s important to note that the number of channels must be constant and known to Punctual when an expression is executed. That means that expressions like unrep (step [2,3] $ saw 0.3) l
are incorrect, as the resulting number of channels would vary through time.
You can still simulate this behavior by building both signals and showing only one of them at any moment:
l << hline [0.9,0.8..(-0.9)] 0.001;
u2 << unrep 2 l;
u3 << unrep 3 l;
s << step [0,1] $ saw 0.3;
u2*s >> add;
u3*(1-s) >> add;
Let’s see some creative ideas around unrep
. In this first example, we start by creating 10 horizontal lines. Using the unrep
function, we then consolidate these 10 channels into 2 groups of 5 channels each.
Next, we define two colors, c1
and c2
. The objective is to assign one color to the first group of lines and the other color to the second group. To achieve this, we use the zip
function to pair the corresponding red, green, and blue components of the two colors.
The *:
operator multiplies each group of lines by its associated set of color components. However, a challenge arises as the color components are paired (two red, two green, and two blue), which would result in an interpretation of rgbrgb
rather than the desired rrggbb
when sent to the rgb
output. To resolve this, we employ the unrep
function once again, grouping the channels into pairs.
l << unrep 5 $ hline [0,0.1..0.9] 0.02;
c1 << [1,0.5,0.8];
c2 << [1,0.4,0];
unrep 2 $ l *: (zip c1 c2) >> add;
Now that we understand how to assign different colors to distinct sets of elements, let’s apply this concept to create a more intricate pattern.
In this example, we start by forming a quadrilateral with dynamically moving vertices. To achieve this, we define coordinates using x
and y
, combining them with zip
. As a result, we obtain two points for each argument in the line function, which, when combined combinatorially, give us four lines.
Building on the previous idea, we color two of the lines with c1
and the remaining two with c2
.
Finally, with the application of a substantial amount of feedback, captivating line patterns emerge.
x << osc [0.11,0.23];
y << osc [0.13,0.17];
l << unrep 2 $ spin (saw 0.03) $ line (zip x y) (zip y x) 0.003;
c1 << [0.5,0.25,0.4];
c2 << [0.5,0.2,0];
fit 1 $ unrep 2 $ l *: (zip c1 c2) >> add;
gatep 0.2 $ 0.99*fb fxy >> add;
In this last example, unrep
is applied to feedback.
Initially, a straightforward pattern of moving lines is created. The feedback is then taken in polar coordinates (refer to Playing with feedback), and it is duplicated using spin, resulting in a 3-way symmetry.
Since the feedback is the last frame image, it always has three channels. After applying spin, the signal expands to a total of nine channels. The unrep 3
bit collapses each copy of the feedback into a single channel. When sent to rgb
, this is interpreted as red, green, and blue, respectively."
tile [4,2] $ line (osc [0.1,0.2]) (osc [0.3,0.4]) 0.02 >> add;
unrep 3 $ spin [0,2/3,4/3] $ (fb frt) * 0.9 >> add;