Geometric transformations

Applying geometric transformations to patterns

Basic transformations

Punctual have two sets of functions for making geometric transformations. The first set are functions that are meant for a specific transformation, while the second set are functions that allow to specify any formula. In all cases, the result is achieved by remapping fx and fy.

In the first set, there are four functions:


  • tile [x,y] ...: repeats the pattern x times across the x axis and y times across the y axis. If only one number is specified, it’s applied to both axis. If a negative number is specified, the corresponding coordinate is flipped.
tile [8,4] $ iline [0.3,0.5] [-0.6,-0.8] 0.02 >> add;

Geometric transformations example 1

Note how the visible pattern in the screen is rescaled and repeated: lines get thiner and are limited to the length of the original pattern.


  • move [x,y] ...: translates the pattern by the specified amount in each axis. If only one number is specified, it’s applied to both axis.
move [0.3, -0.4] $ circle 0 0.1 >> add;

Geometric transformations example 2


  • spin amount ...: rotates the pattern by the specified amount. Here, 1 is half a revolution, and spin amount acts in a clockwise direction (in opposition to degrees or radiants), hence 0.5 is -90º, -0.25 is 45º and so on.
spin (-0.25) $ line 0 [1,0] 0.001 >> add;

Geometric transformations example 3

To convert from radiants (α) to the unit used by spin (s), use the formula: s=-α/π. The reverse is α=-s*π.


  • zoom [x,y] ...: zooms in by x across the x axis and by y across the y axis. If only one number is specified, it applied to both axis. Numbers greater than 1 make the pattern bigger (like looking from a shorter distance), and less than 1 make the pattern smaller (like if we were moving away from the screen). If a negative number is specified, the corresponding coordinate is flipped.
zoom 0.2 $ circle 0 0.1 >> add;

Geometric transformations example 4

Basic transformations examples

Things get interesting when applying some of these ideas to the transformations:

spin (0.8*osc 0.013*osc 0.15) $ tile [3 ~~ 8 $ osc 0.13] $ circle [0,0.5] (unipolar $ tri 2) >> add;
0.9 * fb fxy >> add;

Geometric transformations example 5

(fit 1 $ mono $ zoom [0.8, 0.8, 1.4] $ move [0.2,0.2,0.1] $ spin (saw [0.03,-0.02,0.04]) $ tile [0,8] $ hline 0 0.03) * [0.8, 0.4, 0] >> add;

Geometric transformations example 6

  • Transformations that are different for each fragment by using some of the fragment coordinates.
fit 1 $ mono $ spin (saw [-0.06,0.034]) $ spin (6*ft/(2*pi)) $ tile [6+15*(unipolar $ osc 0.07),1] $ vline 0 0.03 >> add;

Geometric transformations example 7

l << line 0 [1,0] 0.01 >> add;
spin ft l >> red;

Geometric transformations example 8

This example deserves an explanation. How is it possible that the white line turns into 4 lines when applying spin ft?

When we say that spin (or any other transformation) rotates the specified graph we are making a simplification. In reality, each fragment computes its own result. Here, for each fragment, the inverse rotation is computed using its own ft, and the ones that after being rotated get near enough to the line are painted red.

Note that when using a fixed number (like spin 0.3), both ways of thinking lead to the same result. It’s the same to say a line is rotated by 0.3, than saying that all fragments that when rotated by -0.3 are in the line are white. But the latter one is far more confusing and difficult to say.

So, let’s try to explain the 4 red lines. We need to find for which ft, when applying a rotation of -ft the result is 0. Note that ft is computed in radiants, but spin uses its own unit, explained above.

The answer is that this will happen for any ft that’s equal in radiants than in the spin unit. Note that the obvious answer is ft=0 (because 0 is the solution of the ft=-ft/pi equation). But there are other possibilities, because adding any multiple of 2 to the spin is the same angle, and it’s valid as long the result is the range -π to π.

Take for example ft=-ft/pi+2. Group the ft: ft+ft/pi=2; multiply by π and take the common factor: ft*(pi+y1)=2*pi; finally, isolate ft: ft=2*pi/(pi+1). Then, ft ~= 1.517. You can check that spin 1.517 $ line 0 [1,0] 0.01 >> add is the same as one of the four red lines.

Following the same argument, the other three angles are the solution of ft=-ft/pi-2 (-1.517), ft=-ft/pi+4 (3.034), and ft=-ft/pi-4 (-3.034).

The next examples combine some of the previous ideas:

(fit 1 $ mono $ spin [fr*8, (-1)*fr*8] $ hline (tri $ 0.35 / [5,7,9]) 0.1) * [fr/3,0,unipolar $ osc 0.05] >> add;

Geometric transformations example 9

In this first example, we start with three horizontal lines that move vertically at different speeds. By using spin with fr, we bend the lines, and, as we used a two-channel graph inside spin, the result are 6 curves that moves by groups of three in a symmetrical way. Lastly, we apply color: first we crunch all channels together with mono and then multiply the result by the color. Note how the red component depends on the fragment’s radius, and the blue component varies through time.

c << move [tri [-0.13, 0.15], tri [-0.15, 0.16]] $ circle 0 1.2 - circle 0 1;
fit 1 $ spin (fr*0.3) $ spin (ft/pi) $ move [osc fxy * 0.05, osc fxy * 0.09] $ c >> add;

Geometric transformations example 10

Here, we start by building a circumference by subtracting two circles. Then we provide two oscillators to move, with two coordinates each one, and so we have four circles moving around the screen. On the second move, we use oscillators which frequency have two numbers (because fxy is [fx,fy]). That means that each fragments is duplicated and moved differently depending on its coordinates, and this creates the blurring effect. Then, spin is applied twice, using ft and fr. The first spin


The set of functions setfx, setfy and setfxy allows to transform one or both coordinates using any expression. They are a bit more complex to use than the basic transformations, but very flexible.


  • setfx [x...] graph

setfx remaps the x coordinate of a graph by the given formula.

With simple examples, setfx is equivalent to move. These two lines are equivalent:

move [0.2,0] $ vline 0 0.01 >> red;
setfx (fx-0.2) $ vline 0 0.01 >> green;

Geometric transformations example 11

Moving the line by 0.2 to the right is equivalent than taking each fragment and painting it in the color found at 0.2 to the left.

But these remapping functions are a lot more flexible. In the next example, each fragment x coordinate is multiplied by a number between -1 and 1 that depends on its distance to the origin. This operation completely deforms the image creating an interesting pattern:

l << tile [8,1] $ vline 0 0.05;
setfx (fx*sin' (fr*10)) l >> add;

Geometric transformations example 12

The formula to calculate x don’t even need to include the original x coordinate. This is the same example than before, switching fx by fy:

l << tile [8,1] $ vline 0 0.05;
setfx (fy*sin' (fr*10)) l >> add;

Geometric transformations example 13

If there is more than one element in the list, each one creates different channels, so the resulting graph has as many channels as the product of the number of channels in graph by the number of channels in the list.

In this example, the final graph has a total of 6 channels (3*2) that are mapped to a 4-channel output:

c << tile [8,4] $ circle 0 0.8 * [0.8, 0, 0.5];
setfx [fx+fx%0.1, fx*sin' (fy*8)] c >> blend;

Geometric transformations example 14


  • setfy [y...] graph

setfy is exactly the same function than setfx, but it modifies the y coordinate instead of the x one.

Here, setfy is used to evolve a color pattern, creating a symmetric repetition across the vertical axis:

c << [unipolar fx, fr*0.4, 1-abs fx, 0.8];
setfy [sin' (fy*10)] c >> blend;

Geometric transformations example 15

c << fit 1 $ between [0.5+2*px, 0.5-2*px] fr;
mono $ setfy (fy+[0.1,0.2]*osc (fx*0.2)*(sin' $ fx*20)) c >> add;

Geometric transformations example 16


  • setfxy [x,y...] graph

setfxy remaps both the x and the y coordinates of a graph. move, spin, zoom and tile can be rewritten using only setfxy, but setfxy is more flexible, allowing the use of any other mathematical formula.

In this example, each fragment is slightly displaced, and the displacement varies through time at a speed that depends on the fragment’s coordinates. This results in the blurring of the circle outline:

setfxy (fxy +: (osc fxy * 0.05)) $ circle 0 0.5 >> add;

This code is equivalent to move (osc fxy * (-0.05)) $ circle 0 0.5 >> add;.

Geometric transformations example 17

In the following example, a circle c is defined with coordinates (x, y) and radius r. However, x, y and r are determined using distances from moving points, creating a highly distorted and dynamic shape.

To grasp this concept better, let’s start with a simplified code:

x << dist [0.2,0.2];
circle [x,0] 0.2 >> add;

Geometric transformations example 18

Here, for each fragment (fx, fy), the distance x between the fragment and (0.2, 0.2) is computed. Then, the fragment is painted white if the distance between the fragment and the point (x, 0) is less than 0.2.

In the final example, x is calculated in this manner but with a moving point. y is similar, but it uses the “proximity” instead of distance. Additionally, r is also dynamic and computed similarly to x.

The computed values of x, y, or r for each fragment can be visualized by sending them to the output.

The resulting circle can be visualized with this code:

x << dist $ osc [0.3, 0.2];
y << prox $ osc [0.13, 0.1];
r << dist $ osc [0.11, 0.19];
circle [x, y] r >> add;

Geometric transformations example 19

The final shape is built by applying the transformation setfxy (abs fxy) to the previous circle.

To understand the effect of this transformation, let’s study this simplified code:

c << circle [0.5,0.2] 0.02;
setfxy (abs fxy) c >> add;

Geometric transformations example 20

Here, the absolute value of fx and fy is computed. If the result is near enough to (0.5, 0.2), the fragment is white; otherwise, it is black. This results in a circle in the first quadrant (both coordinates positive) being duplicated in each of the other quadrants. Note that if the circle has any negative coordinate, the result is a black screen, as there exists no number that results in a negative value when applying abs.

With this analysis, we can now understand that this setfx transformation is creating a four-fold symmetry from the content in the first quadrant.

The rest of the example involves details. fit 1 is applied to the resulting shape in order to make fragments square, and mono is used to put the four copies of the shape in the same channel.

Finally, some color is applied to the shape before sending it to the output, and feedback is used to create the impression that the shape is moving smoothly:

x << dist $ osc [0.3, 0.2];
y << prox $ osc [0.13, 0.1];
r << dist $ osc [0.11, 0.19];
c << circle [x, y] r;
shape << mono $ fit 1 $ setfxy (abs fxy) c;
color << [0, fr/2, 0.5, 0.7];
shape * color >> blend;
gate 0.1 $ 0.98 * fb fxy >> add;

Geometric transformations example 21

In the following example, setfxy is employed twice: first to establish a horizontal symmetry and then to transform the pattern into polar coordinates.

The pattern begins by defining x and y as two rotating gradients from black to white.

These gradients x and y are combined to create a more complex color pattern in co. This pattern is then transformed into sh1 by applying horizontal symmetry and further converted into polar coordinates, stored in sh2. sh3 represents a version of sh2 that is moved and rotated.

Finally, the color in sh3 is transformed into the HSV colorspace and sent to the output, with the hue interpreted as red, the saturation as green, and the value as blue, resulting in the final color pattern.

x << unipolar $ fit 1 $ spin (saw (-0.013)) fx;
y << unipolar $ fit 1 $ spin (saw 0.01) fy;
co << 10*([x,y,x+y]%0.08);
sh1 << setfxy [fx, abs fy] co;
sh2 << setfxy frt sh1;
sh3 << move [0,0.45] $ spin (-0.5) sh2;
hsvrgb sh3 >> add;

Geometric transformations example 22

In the following pattern, setfxy is utilized to convert to polar coordinates, followed by a simple geometric transformation, and then another application of setfxy to revert to Cartesian coordinates.

The initial segment of the pattern builds upon one of the examples seen in 10 ways of drawing a circumference of radius 1. The key variation here is the addition of a value to the radius at each point on the circumference. This value corresponds to the intensity of the frequency that aligns with the angle of the point, as defined by the formula in a.

In a, the possible angle range is mapped to [0, 0.7], representing audible frequencies except the highest. abs ft is employed to create symmetry between the upper and bottom halves of the circumference. Finally, the ** 3 operation cubes the result, reducing the amount added to the radius.

The subsequent segment of the pattern takes this result, translates it to polar coordinates, duplicates it using tile, and then reverts the result back into Cartesian coordinates. This technique enhances simple transformations when applied within polar coordinates.

a << (linlin [0,pi] [0,0.7] $ abs ft) ** 3;
b << 1+ifft a;
c << between [b-px,b+px] fr;
d << tile [8,4] $ setfxy frt c;
setfxy (rtxy fxy) d >> add;
0.9*fb fxy >> add;

Geometric transformations example 23

The last example expands upon the previous one, retaining the initial setup with minor adjustments to the circumference stroke width. Afterward, the circumference is translated into polar coordinates, and color is applied.

The tile operation undergoes slight modifications, introducing irregularity by incorporating fx into the calculation determining the number of fragment repetitions. Additionally, a new basic transformation, a constant velocity spin, is introduced.

Further irregularity is introduced through a move operation, with one of its values determined by an oscillator driven by the audio frequency. Visualizing f reveals a series of curved lines that move dynamically across the screen in response to the music.

The subsequent steps involve translating the result back from polar coordinates to Cartesian coordinates and applying a substantial amount of feedback.

a << (linlin [0,pi] [0,0.7] $ abs ft) ** 3;
b << 1+ifft a;
c << between [b-4*px,b+4*px] fr;
d << (setfxy frt c)*hsvrgb [fr,fx,fy];
e << spin (saw 0.1) $ tile [1+3*fx,4] d;
f << move [saw imid, saw 0.035] e;
setfxy (rtxy fxy) f >> add;
0.99*fb fxy >> add;

Geometric transformations example 24