Distorting things with CLJS

Sin wave distortion sample

A while ago I learned from a good friend and fellow Clojurian about a thing called "generative art". He showed me a few examples from different artists, which got me curious, and since I had never heard the term before I started looking for a more extensive introduction to that topic. I found a very interesting blog post by Amy Goodchild that nicely explains the different paths down the rabbit hole of generative art. So far I have only explored a tiny fraction of this universe, but after seeing so many interesting works, I felt the urge to try and create something myself. At that point, I only had a vague idea of an animation involving geometric patterns in mind, but first things first, before unleashing my inner Dalí I needed to prepare the canvas :)

Technology

I began by setting up a new shadow-cljs project and immediately added quil as a dependency. Quil is a Clojure wrapper around Processing, a graphical library that is beginner friendly and a popular choice for creating visual art. Quil provides an interface to both the Java version, as well as the JavaScript version of Processing and was perfect for me to get started. I warmly recommend reading this marvelous description of what quil is.

Since I wanted to create something that moves, I also included reagent as a dependency so I would be able to conveniently handle the animation state later on.

Getting Started

While reading through a Wikipedia article about optical distortion, inspiration struck and I had an idea where to get started with my animation. The article describes two types of distortion that are quite common for zoom lenses in photography: pincushion distortion and barrel distortion. By using illustrations and some animation, the article already does a good job explaining what these distortions look like and I was curious if I could produce a similar visual effect as described there.

As a first step, I created a regular 2D diamond pattern as a base. This grid of straight lines is suited well to observe any distortion effects which I wanted to create.

Basic 2D diamond pattern without distortion

As a second step, I wanted to apply some sort of distortion to this diamond pattern, causing the straight lines to bend.

How to model this distortion?

What both of the distortions mentioned in the Wikipedia article had in common was that they are radial distortions. This means that they are rotational symmetric around a center point, which is due to the symmetric nature of photographic lenses. Therefore I could model the distortion as a simple linear function of a center point and the distance between the center and the pixel I want to distort.

Illustration of distance to center point
Doing a barrel roll distortion

Next, I had to find a suitable function that maps the distance between the image points and the center of distortion in such a way that it would produce an optical effect similar to that of a barrel distortion. Remembering vaguely my 8th-grade math classes I tried to reproduce the desired "barrel shape" by using a Gaußian bell curve. In trial and error fashion I played around with the parameters until I reached acceptable results. For the pincushion distortion, I basically did the same thing, just using a sigmoid curve instead of a Gaußian bell curve. I ended up with the following distortion functions:

(defn gaussian [sigma mu x]
  (* (/ 1 (* mu (Math/sqrt (* 2 Math/PI))))
    (Math/exp (- (/ (Math/pow (- x sigma) 2) (* 2 (Math/pow mu 2)))))))

(defn barrel-like-distortion [undistorted]
  (* 5000 (gaussian 30 45 undistorted)))

(defn sigmoid [x mid steepness]
  (/ 1 (+ 1 (Math/pow Math/E (* (- steepness) (- x mid))))))

(defn pincushion-like-distortion [undistorted]
  (- (* (- 1 (sigmoid undistorted 50 0.1)) undistorted)))

To make the whole thing a little more visually appealing and to put emphasis on the distortion centers, I also added a color scheme consisting of three different colors that are applied depending on the distance from the center of distortion.

Distortion pattern similar to barrel distortion
Make it move

By adding the shadow-cljs ^:dev/after-load lifecycle hook annotation to the render function any changes I made to the distortion functions were immediately reflected in the sketch after every hot code reload. Fun so far, but what I actually wanted to create was not a static image, but some sort of dynamic animation, where the distortion pattern constantly changes. To make things dynamic, I wanted to make the center of distortion move across the canvas, instead of staying at the center of the image all the time. This meant to introduce state to the animation, which I did by adding a r/atom that defined the center of distortion in polar coordinates by radius and angle from the coordinate origin.

(def state (r/atom {:f #'barrel-like-distortion
                    :center {:angle 0
                             :r 150}
                    :update-rate 500}))

Next, I added an update function that would simply move the center of distortion a notch further along its circular path by increasing the angle slightly depending on the update rate configuration. How often this happens can be configured via Quil's frame-rate parameter which is defined in frames per second.

(defn move [distortion]
  (update-in distortion [:center :angle]
    #(mod (+ % (/ Math/PI (:update-rate distortion))) (* 2 Math/PI))))

(defn update-state [state]
  (swap! state update :distortions #(map move %))
  state)

That was it for animation, the center of distortion was now slowly moving clockwise across the canvas, distorting the diamond pattern on its path.

Visual Debugging

While trying out new functions, e.g. by adding a sin wave or multiplying by a logarithmic factor, it often happened that I ended up with crazy distortion patterns that I could not really explain. To reach a better understanding I used hiccup-d3 to build some additional "debug-visualizations". I used the d3 line chart to do some simple plots of the distortion functions, which helped me to understand how the functions affected the animation at certain radii. In the example plot below the x-axis indicates the distance from the center and the y-axis indicates if the distortion function produces a positive or a negative summand, which is then added to the original distance. In other words: positive y-values mean points are moved further away from the center, whereas negative y-values mean that points are moved closer to the center compared to their original undistorted positions.

Barrel distortion next to the function plot
More more more

Now that I got all the moving parts in place and a neat little helper view for debugging, I started experimenting some more. How about adding multiple distortions at once? This way I could see how the different distortion functions interact with each other and create new interesting effects in combination. To enable multiple distortions within the same sketch, I switched the animation state to become a vector of distortion maps, each representing a separate distortion with an independent animation cycle. After also adapting the render functions, I could simply add or remove distortions, or change update rates and still see the immediate visual feedback. Here are some of the distortion combinations that I liked in particular: A barrel and a pincushion distortion that both move clockwise, but the pincushion distortion moves a little faster and 'swallows' the barrel distortion while moving over it.

A sine wave distortion intersecting over and over again with a faster barrel distortion.

Thanks for reading and I hope you enjoy the animations and how they were created. In case you are curious, you can find the complete source code here and if you have a look at it, I'm always grateful for improvement suggestions :)