Reproduceable Generative Art with Quil and Clojure


I've been making generative artwork in Clojure using Quil for just over 2 years now. Over time I've evolved a workflow that enables me to experiment quickly whilst reducing the risk of losing interesting results and avenues for exploration.

Most of the artwork I make is static images, usually with the intention of plotting them using an AxiDraw. I usually export images as both png for previewing (and sometimes printing) and svg for plotting. I am also gradually developing a small library with a focus on manipulating shapes and lines.

I work in Emacs connected to a repl using CIDER, which means that evaluating the current buffer generates a new sketch.

It's often useful to be able to reproduce a particular output, and the way I've been approaching this is to replace Clojure's random functions with an implementation where the seed can be set, as well as setting the seed for Quil's own random and noise functions.

I'd been exporting images with the random seed in the filename for a while, but as I tend to continually tweak the code, it often isn't enough to recreate that exact output. I wanted a simple way to save the state of the source code as well.


So far I've settled on something like this1:

(ns sketch
	[quil.core :as q]
	[ :as io]
	[random-seed.core :refer :all])
  (:refer-clojure :exclude
	[rand rand-int rand-nth shuffle]))

(defn export []
  (let [name (str sketch-name "-" seed)
        src (:file (meta #'export))
        dest (str "output/code/" name ".clj")
        png (str "output/png/" name ".png")
        svg (str "output/svg/" name ".svg")
        gr (q/create-graphics
		(q/width) (q/height) :svg svg)]
    ;; Save source
    (io/make-parents dest)
    (io/copy (io/file src) (io/file dest))
    ;; Save png
    (set-random-seed! seed)
    (q/save png)
    ;; Save svg
    (set-random-seed! seed)
    (q/with-graphics gr
      (.dispose gr)))

Exporting a sketch, including copying the source, now means replacing the draw function in the sketch with this export function, without the need for extra infrastructure and setup. It does require that a) there is a single file we want to save a snapshot of and b) the export function is within that file (as it uses the metadata of the export function itself) - but that suits me well at this point.

  1. I initially worked with Quil's functional-mode middleware (because Clojure), but moved away from it as I do all the rendering in a single draw call anyway.

Read More