Oscillators
An oscillator is a device that generates oscillations:
the repetitive or periodic variation, typically in time, of some measure about a central value (often a point of equilibrium) or between two or more different states. — https://en.wikipedia.org/wiki/Oscillation
In Punctual, an oscillator is a function that returns values in the range from -1 to 1, varying through time.
All oscillators take a single argument: the oscillation frequency in hertz (full cycles per second).
osc
, tri
, saw
, sqr
There are four oscillators in Punctual, each with a different pattern:
In the next example, you can see all four oscillators in action. There are four balls, each one moving according to one of the oscillator types along the y axis, and using the same colors as in the previous representation. Each cycle lasts 4 seconds instead of 1:
fit 1 $ (circle [-0.6, osc 0.25] 0.1 * [1,0,0] +:
circle [-0.2, tri 0.25] 0.1 * [0,0,1] +:
circle [0.2, saw 0.25] 0.1 * [1,0,1] +:
circle [0.6, sqr 0.25] 0.1 * [0,1,0]) >> add;
The first circle, in red, uses the osc
function and follows a sinusoidal wave. The second one, in blue, uses tri
and follows a triangle waveform. The third one, in magenta, uses saw
and follows a saw wave, that is, it increases its value from -1 to 1, and then jumps and starts again at -1. The last circle, in green, uses sqr
and follows a square wave, taking only the values -1 and 1 for half a cycle each one.
Oscillators are such a fundamental tool in Punctual that you can find examples of them being used across all the sections in this guide.
Summarizing, oscillators can be used to modulate:
- Coordinates
x1 << osc 0.13;
y1 << osc 0.15;
x2 << osc 0.09;
y2 << osc 0.11;
line [x1,y1] [x2,y2] 0.001 >> add;
fit 1 $ circle [x1*x2, y1*y2] 0.2 >> add;
line [tri fx, tri fr] [tri fy, tri fr] 1 >> add;
- Transformations
l << [5 .. -6]/100;
mono $ spin (saw l) $ hline 0 0.001 >> add;
- Colors
fit 1 $ circle 0 1 * (0.5 ~~ 1 $ sqr [0.73, 0.81, 0.65]) >> add;
- Sizes
fit 1 $ circle 0 (2*:(unipolar $ sqr (0.5+fx*fy*5))) >> add;
hline 0 (0.2*(abs $ osc (0.2*abs fx))) >> add;
- Other oscillator frequencies
0.99 * fb >> add;
f<<[0.1,0.11..0.2];
(mono $ circle [osc (0.01*:osc f), osc (0.01*:osc f)] 0.1) * [1.2-fr,0,0.6,0.3] >> blend;
- Between a set of values (see
seq
below)
Changing the phase of an oscillator
The phase is the position on the cycle of an oscillator where it begins its movement. Two oscillators can have the same frequency and amplitude, but different phase, and then one is displaced respect the other:
At the moment, the easiest way to change the phase of an oscillator is by using early
or late
:
o << osc 0.1;
(between [-1,0] $ fx) * o >> add;
(between [0,1] $ fx) * (late 5 o) >> add;
Here, the o
oscillator has a frequency of 0.1, that is, it completes a cycle every 10 seconds. The second version has the same frequency, but it starts 5 seconds later, so it will be in antiphase with the first one.
The same idea can be applied to the other oscillators.
o << tri 0.1;
(between [-1,0] $ fx) * o >> add;
(between [0,1] $ fx) * (late 5 o) >> add;
o << saw 0.1;
(between [-1,0] $ fx) * o >> add;
(between [0,1] $ fx) * (late 5 o) >> add;
o << sqr 0.1;
(between [-1,0] $ fx) * o >> add;
(between [0,1] $ fx) * (late 5 o) >> add;
seq
seq
is a function that acts as a step sequencer. It takes a list of expressions as an argument and iterates through them, choosing one at a time based on the phase of a saw oscillator synchronized to the cycle. This allows for creating rhythmic or patterned variations over time. For example, you can use seq
to alternate between different positions, colors, or other properties in a cyclic manner.
s << seq [-0.5,0,0.5];
vline s 0.001 >> add;
Here, the vertical line position changes regularly, taking the values in the list by turns.
Use slow
or fast
to change the speed of the sequencer, and late
or early
to change the phase:
s << fast ([1,2]/7) $ seq [-0.5,0,0.5];
spin (late [1,2] s) $ vline s 0.3 >> add;
Next, there is a more complex example. x
and y
define the center coordinates of a circle. Due to the fast oscillators controlling seq
, the circle jumps between four moving points in the screen. r
is the circle radius, which changes over time between three values. c
is the circle’s color, which is changing over a reduced set of possibilities, due to the three seq
functions using different frequency oscillators. Note how the alpha channel is set to 0.7. This, with the feedback set to a full 1, allow the circles in each frame to accumulate on the screen without saturating the color (this technique is explained in the color section), creating the effect that there are four circles moving:
fb >> add;
x << osc 0.17*osc 0.19*(fast 10.2 $ seq [-1,1]);
y << osc 0.16*osc 0.18*(fast 10.6 $ seq [-1,1]);
r << fast 0.34 $ seq [0.1,0.2,0.3];
c << [fast 10 $ seq [0.5,1], fast 108 $ seq [0,0.5,1], fast 114 $ seq [0,0.5,1], 0.7];
fit 1 $ circle [x,y] r * c >> blend;
seq
is not limited to single numbers. You can use it to iterate over a list of expressions:
c << fit 1 $ circle 0 0.5;
c2 << slow 4 $ seq [zoom 2 c, move [0.5,0] c];
c2 * [0.3, 0.6, 0.8] >> add;
It is even possible to use multi-channel expressions:
c << fit 1 $ circle 0 0.5 * [0.3, 0.6, 0.8];
c2 << slow 4 $ seq {zoom 2 c, move [0.5,0] c};
c2 >> add;
This last example is equivalent to the previous one, but signals inside seq
are multi-channel expressions, as we have applied color to the circle before the seq
function. The use of curly braces {}
is necessary to correctly mix the signals.
i1 << img "https://upload.wikimedia.org/wikipedia/commons/b/b4/Vaporwave_for_China.jpg";
i2 << img "https://upload.wikimedia.org/wikipedia/commons/4/48/Mao_Tse_tung_in_1965_Color.png";
i3 << img "https://upload.wikimedia.org/wikipedia/commons/b/b6/Mao_Zedong_in_front_of_crowd.jpg";
slow 4 $ fit (seq [1,0.66,0.86]) $ seq {i1,i2,i3} >> add;
spr
, sprp
spr
(for spread) is similar to seq
, but you can define the function that chooses the next value. It takes two arguments: the list of values and the function that will choose the next value. sprp
is the pairwise version of spr
.
The following code is equivalent to the first example using seq
:
s << spr [-0.5,0,0.5] $ saw cps;
vline s 0.001 >> add;
spr
and sprp
are useful when you want to use another oscillator instead of saw
, when you don’t want to align the oscillator to the tempo, or when you want to use a custom function to choose the next value.
Evolving from the last example, we can now start to apply transformations to the feedback. Here, we use spr
to apply a spinning effect. But this time, the amount of spinning applied depends on the distance from the center (fr
), so fragments on the center rotate to the right, fragments a bit more distant rotate slower to the left, more distance ones to the right again, and the most distant don’t rotate at all:
zoom 1.003 $ spin [spr [0.03,-0.005,0.01,0] $ (bipolar fr)] fb >> add;
x << osc 0.17*osc 0.19*(fast 10.2 $ seq [-1,1]);
y << osc 0.16*osc 0.18*(fast 10.6 $ seq [-1,1]);
r << fast 0.34 $ seq [0.1,0.2,0.3];
c << [fast 10 $ seq [0.5,1], fast 108 $ seq [0,0.5,1], fast 114 $ seq [0,0.5,1], 0.7];
fit 1 $ circle [x,y] r * c >> blend;