The past few weeks have been one of these wild, unexpected frenzies of
hacking and art, punctuated and intermingled with the occasional days of
schoolwork. As the primary larval stage
seems to be fading, I feel like I must note down these early events.
On Saturday June 20, I came across Google's Inceptionism, which has eventually become a rather popular piece of scientific news. While it would have been interesting and somewhat familiar anyway, the final notes about iteration really hit it with the exact topic of the fractal course. Then on Sunday evening, needing a little air from all the studies, I was walking on the nearby Kortesuo beach. I took the foresty passage right next to water, and as sunlight filtered down through the arching twigs, I remember thinking: who needs art museums with a nature like this? I soon ran into DJing friend, and we had a brief discussion on the article and its iterative connotations.
A key idea with iterated function systems is that the final picture is independent of the initial data; it is simply a function of the iterating functions themselves. We wondered if there is any need for an external reality, given that human perception does the same kind of iterations on any input. On the other hand, the IFS theory involves an infinite number of iterations, and human perception must be relatively low-latency to be of any use. Nevertheless, all this triggered memories of psychedelic and spiritual experiences, mostly from my days in Cambridge, as a much needed reminder of the wonderfulness of the world around.
One of the day's math exercises was to find a simpler, alternative IFS for the von Koch snowflake curve. The answer was relatively complex with 2x2 matrices, and with all the different signs and factors, I wanted a numerical way to check it, as I often do with such problems. In a classical showering moment, I realized how easy it would be to write the necessary code, given that the plotting side could be delegated to trusty old Gnuplot. On the same night, I soon had my numerical and graphical verification, along with a code general enough to play around with all other IFSes of the course.
Monday afternoon, I started playing with the code a little more. The course examples had been simple linear functions, but the IFS theory was not thus limited, as long as the functions were contractive. Plus, with a numerical sandbox, I could try any random functions and see what happened. However, a sensible choice would be to keep the functions somehow limited, lest the picture blow up in an unbounded mess. So I started with some sines and cosines, occasionally skewed by exponentials. So far, this was all in component-wise R2, due to having started with the linear matrix expressions.
The first attempts must have been meaningless and messy, but soon something quite different emerged. In a mild awe, I was staring at a clear bunch of fish and cephalopods. Changing a single angular parameter turned them into knives, airplanes or birds. I was witnessing evolution in action. I knew I was on to something there, something nobody else had quite done or seen before.
Of course, the idea of finding life-like or otherwise staggering images in simple mathematical graphs is not new or unique per se, since at least the Game of Life. Other familiar examples include common fractals such as Julia and Mandelbrot sets, which can be used as analogies to DNA and evolution, where complex form arises from simple formulae. One personal favourite of mine is Tupper's formula that, in a sense, draws a graph of itself, although the picture is actually pre-built into a constant.
In any case, I had discovered a rather personal, new way of making graphic art, and I was instantly hooked. I have never considered myself good at drawing, though I probably have some sense of visual design. Moreover, as I spent the spring away from any theatre projects, I was considering some kind of solo artistic pastime. I even bought myself a Wacom drawing tablet at some point, though I've barely even done any test drawings so far. I guess with my generally quirky background, the solo art career was bound to get a different form. I couldn't draw my way out of a paper bag, but I can make it up with math and coding.
Given how the pictures I have posted to friends so far have gained huge positive attention, I think I can safely call this art; some of these friends are professional musicians, art critics and performing artists. It wasn't too long until some of them suggested I should arrange an exhibition. It felt rather curious getting such feedback after about a week or two of doing this, considering the years and years I've put into music and theatre, for example. But I guess this just proves I've actually found something rather new and different, so I've started to take these suggestions more seriously.
Speaking of pictures, I modified my old photo gallery script to show a few examples in a dedicated environment. The Julia sets are a rather recent addition; I would not call them original art in the same sense, but my custom colour IFS method puts them into a new light.
Indeed, the code I've been developing all along is an equally artistic part of this whole endeavour. It has also been interesting to see how some of my early, hackish design choices have lived on and gained new, important meanings. For example, one early feature is called "animation" mode, and it simply saves the picture of every iteration instead of just the final one. I imagined I would use them to make animated presentations of the idea of iteration for lay audiences. In practice, my first uses were simply laying the pictures on the same page, as the course handout could use such illustrations. I wasn't going to do any illustrations there to begin with, but my code was too good to be left unused, and it now contains plenty of my imagery.
More notably, the "anim" mode is used as the basis for the current colouring process. The usual b/w scheme comes naturally off basic math graphing, and I'm quite fond of the pencil-like shades that a lot of works end up with, but of course I wanted more variation and dimensions for expression at some point. I've even considered some kind of hand-colouring (possibly with the Wacom tablet), especially after recent news about adults' colouring books, but also to give a more human final touch. This is of course something I may still do later, but I really wanted an algorithmic colouring process to complement the initial algorithmic artistry.
The use of the "anim" mode was an easy thing to do with Gnuplot, and I liked the first results so much that it's become almost a primary tool — even for b/w pictures via grayscale palettes in some cases. Turning the initial idea into something more flexible and tweakable has been a nice little exercise in shell scripting, and the colourspace is a world in itself. I've barely even started writing my own palette functions. It's a healthy reminder of my lack of any drawing/graphics experience, that I now have to learn about things like the HSV space in more detail.
The way in which this colour process uses iterations is not unlike the common escape-time plots of Julia and Mandelbrot sets in spirit. The results look very different, though, as the different iterations are overlaid, rather than colouring each point separately by its iteration number. The shape and size of the initial set also becomes more apparent, and varying them can be essential effects in their own right.
Speaking of escape-time graphs, as I got into the course handout illustrations, I ended up having to write the traditional Julia-set code as well. This wasn't much of a job, of course, especially as I didn't need the colouring part. However, I did write that too at some point as I was planning the IFS colour process, as it helped me understand how Gnuplot deals with colours and palettes. On a more general level, it got me into thinking how colour could be used as a separate dimension in the IFS for wild and interesting results, but that's quite a different direction I won't take quite yet.
One reason for sticking with pure 2D is the way Julia and Mandelbrot sets are native to the complex plane. In fact, it was very soon after my first IFS art that I changed the main code from R2 to C for good. Everything can still be done componentwise if need be; the old functions are easily converted by making them act on the Re and Im parts separately. In fact, most of my picture functions still have some component-wise parts to make things a little quirky. But on a general level, I like the elegance and compactness of purely complex-analytical functions. It provides kind of soft limits within which to play freely while having some rigour.
Code optimization is also a matter of eternal interest to me, though it did not seem obvious at first. However, as I first started thinking of exhibitions, I decided to make some A3 prints for demo purposes, and with some pictures this meant gigs of memory and hours of processing. Even if I could parallelize it a bit, it would not solve anything fundamentally. So one day, walking home with a backpack full of groceries, I strolled past a print shop and it got me thinking: I'm going to need a new machine to make proper A2 sized posters, as the current code is already hitting limits with my maxed-out 8 GB machines.
I soon realized, since the points of the initial set are independent, I could process some of them on another machine, and then simply combine the resulting datafiles. That would already solve the problem to some extent, though it would take a little work to organize it. But it would be very easy to do on a single machine, thus parallelizing work without increasing the memory load.
But the memory load would still be an issue. Then the obvious solution hit me: instead of parallelizing work, I should serialize it. By processing these batches of work sequentially, I could drastically reduce the memory load. In effect, I could build the picture from thin layers, instead of keeping it all in memory at once. And then, having slashed the RAM needs, I could also run the batches in parallel without ill effects. In essence, I had solved a parallelization problem indirectly by serializing it first.
In fact, the serialization itself turned out a huge success in optimization. It made the code about 5x as fast, while doing the same calculations on a single CPU. I guess it was down to some hairy factors in the OS level, as other processes start giving way, or perhaps in Julia itself. Nevertheless, a surprising but effective solution.
Speaking of Julia, the language, it was also one of these early choices that turned out hugely important later. I did try Python briefly at one point, but it simply wasn't suitable without some extra libraries. The thing about Julia I came to appreciate the most this time are the ways of defining and handling functions. I guess the entire thinking about functions is greatly affected by my past math courses, notably Functional Analysis, where functions are considered objects to be handled just like numbers. I now use a number of custom metafunctions that take functions as arguments, to define custom functions more easily. While these things can be done in any decent programming language, Julia makes it much more convenient with its math-influenced notation. For example, a neat way to define a function
f(x) = 2x + 1or a set of functions:
ifs = [x -> sin(x), x -> cos(x) + c]
One known downside of Julia is its slow string handling. I struggled with this for a while at some point, as this is rather essential to my code: I need to write huge datafiles for Gnuplot. There are libraries being developed for direct Gnuplot access, but as I couldn't get one working as well and easily as I'm used to with Gnuplot in general, I soon gave up.
I eventually found a workaround. At some point, I was simply print()ing each line in succession, and redirecting it to file via the usual ways of shell. My file-writing alternative would involve building the file contents into a long string and writing it at once, which was much slower for some reason. So print()ing would be a better option, but it would have the downside of not being able to display anything else while processing. It turned out I could simply write() the same lines in a loop into a file, at the same speed as print()ing, as it was essentially the same function. In effect, as the bottleneck was in concatenating the individual lines, I let the OS/filesystem handle it, as it does it much better than Julia.
At this point, I am somewhat torn between different projects. I have a minor thesis to write, and a new theatre project is also starting up. Fortunately, I also feel the need for a little break from this art project. While it's been enormously fun, the addictive cycle is also frustrating in some ways — I feel like I need to surpass myself in every new picture. OTOH, I'm also keeping some ideas and variations for the future. For example, the first "evolution" series is too big to show online in any proper form, but would be great exhibition material. Naturally, my near future decisions will depend on securing an exhibition. In some ways I guess I'm afraid this is too good to be true, and this artistic wonder will fade as quickly as it started. Though it must be said that this is a kind of crystallization of years and years of coding and artistic thinking. A little time off might actually help me break some patterns and get a fresher look the next time I return to this.