This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Oscillators

Creating variation through time using 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:

    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 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 << [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;
    
    • 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
    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 >> add;
    

    Oscillator example 8

    • Between a set of values (see seq 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, 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;