Oscillators

Creating variation through time using oscillators.

An oscillator is a device used to create 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 receive a single argument, which is the oscillation frequency in hertzs, that is full cycles per second.

osc, tri, saw, sqr

There are four oscillators in Punctual, each with a different pattern:

Oscillators

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 than 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;

Oscillator example 1

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;

Oscillator example 2

line [tri fx, tri fr] [tri fy, tri fr] 1 >> add;

Oscillator example 3

  • Transformations
l << [0.05,0.04..(-0.06)];
mono $ spin (saw l) $ hline 0 0.001 >> add;

Oscillator example 4

  • Colors
fit 1 $ circle 0 1 * (0.5 ~~ 1 $ sqr [0.73, 0.81, 0.65]) >> add;

Oscillator example 5

  • Sizes
fit 1 $ circle 0 (2*:(unipolar $ sqr (0.5+fx*fy*5))) >> add;

Oscillator example 6

hline 0 (0.2*(abs $ osc (0.2*(abs $ fx)))) >> add;

Oscillator example 7

  • Other oscillator frequencies
f<<[0.1,0.11..0.2];
(mono $ circle [osc (0.001*:osc f), osc (0.001*:osc f)] 0.1) * [1.2-fr,0,0.6,0.7] >> blend;
0.99 * fb fxy >> add;

Oscillator example 8

  • Between a set of values (see step bellow)

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:

Phase

At the moment, there is no an immediate way to change the phase of an oscillators, but we can do it by rewriting the oscillators as mathematical functions.

osc 0.1 is equivalent to sin' (0.1*time*2*pi), so we can add some value inside the sine to change the phase:

(between [-1,0] $ fx) * osc 0.1 >> add;
(between [0,1] $ fx) * sin' (pi+0.1*time*2*pi) >> add;

Oscillator example 9

tri 0.1 is equivalent to 4*abs (((0.1*time-0.5)%1)-0.5)-1:

(between [-1,0] $ fx) * tri 0.1 >> add;
(between [0,1] $ fx) * (4*abs (((0.1*time)%1)-0.5)-1) >> add;

saw 0.1 is 2*((0.1*time-0.5)-floor (0.1*time)):

(between [-1,0] $ fx) * saw 0.1 >> add;
(between [0,1] $ fx) * (2*((0.1*time)-floor (0.1*time+0.5))) >> add;

sqr 0.1 is (-1) * (sign $ sin' $ 2*pi*time*0.1):

(between [-1,0] $ fx) * sqr 0.1 >> add;
(between [0,1] $ fx) * ((-1) * (sign $ sin' $ 2*pi*(time+5)*0.1)) >> add;

step

step chooses a value from a set based on an expression, usually (but not necessarily) an oscillator:

vline (step [-0.5,0,0.5] $ saw 0.3) 0.001 >> add;

Oscillator example 10

Here, the vertical line position changes regularly, taking the values in the list by turns.

Next, there is a more complex example. x and y define the center coordinates of a circle. Due to the fast oscillators controlling step, 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 step 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:

x << osc 0.17*osc 0.19*(step [-1,1] $ saw 5.1);
y << osc 0.16*osc 0.18*(step [-1,1] $ saw 5.3);
r << step [0.1,0.2,0.3] $ osc 0.17;
c << [step [0.5,1] $ tri 5, step [0,0.5,1] $ tri 54, step [0,0.5,1] $ tri 57, 0.7];
fit 1 $ circle [x,y] r * c >> blend;
fb fxy >> add;

Oscillator example 11

You can use step with any other expression in addition to oscillators.

Evolving from the last example, we can now start to apply transformations to the feedback. Here, we use step once again 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:

x << osc 0.17*osc 0.19*(step [-1,1] $ saw 5.1);
y << osc 0.16*osc 0.18*(step [-1,1] $ saw 5.3);
r << step [0.1,0.2,0.3] $ osc 0.17;
c << [step [0.5,1] $ tri 5, step [0,0.5,1] $ tri 54, step [0,0.5,1] $ tri 57, step [0.1,1,0.3] $ saw 0.59];
fit 1 $ circle [x,y] r * c >> blend;
zoom 1.003 $ spin [step [0.03,-0.005,0.01,0] $ (bipolar $ fr)] $ fb fxy >> add;

Oscillator example 12

As you can see from these examples, step is a very useful function and can be used in a lot of different contexts. However, step has some strong limitations:

  • step doesn’t currently support multi-channel signals in any meaningful way.

While next example will work as expected,

c << fit 1 $ circle 0 0.5;
c2 << step [zoom 2 c, move [0.5,0] c] $ saw 0.2;
c2 * [0.3, 0.6, 0.8] >> add;

Oscillator example 13

any attempt to pass a multichannel signal to step will result in strange behavior, as step iterates through the channels instead of taking them as a whole:

c << fit 1 $ circle 0 0.5 * [0.3, 0.6, 0.8];
c2 << step [zoom 2 c, move [0.5,0] c] $ saw 0.2;
c2 >> add;

Oscillator example 14

In this last example, we can skip this limitation by applying color as the last step of the expression, but let’s suppose we want to use step to change between two images, which inherently have 3 channels:

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";
fit (step [1,0.66] $ saw 0.3) $ step [i1,i2] $ saw 0.3 >> add;

Oscillator example 15

The result is quite cool but not necessarily what we expected. Sometimes, there are workarounds that we can use to get to the desired result:

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";
s << step [1,0] $ saw 0.3;
i << i1 * s +: i2 * (1-s);
fit (step [1,0.66] $ saw 0.3) $ i >> add;

Oscillator example 16

Here, s is used to choose one of the two images, through some arithmetics.

What if we want to iterate over three or more images? Here is a scalable workaround:

i1 << (step [1,0,0] $ saw 0.3) * img "https://upload.wikimedia.org/wikipedia/commons/b/b4/Vaporwave_for_China.jpg";
i2 << (step [0,1,0] $ saw 0.3) * img "https://upload.wikimedia.org/wikipedia/commons/4/48/Mao_Tse_tung_in_1965_Color.png";
i3 << (step [0,0,1] $ saw 0.3) * img "https://upload.wikimedia.org/wikipedia/commons/b/b6/Mao_Zedong_in_front_of_crowd.jpg";
fit (step [1,0.66,0.86] $ saw 0.3) $ (i1+:i2+:i3) >> add;

Also:

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";
n << step [0,1,2] $ saw 0.3;
fit (step [1,0.66,0.86] $ saw 0.3) $ ((n==0)*i1)+:((n==1)*i2)+:((n==2)*i3) >> add;
  • List expansions aren’t supported inside step.
circle 0 (step [0,0.1..0.6] $ saw 0.1) >> add; -- error