Nov 26, 2020

My Quil Setup

Quil is a very simple Processing wrapper for Clojure. The writeup below is mostly code-snippets and my personal workflow.

I usually work on animations and bigger projects where I like to embed sketches that act as a renderer. I don’t usually focus on real-time animations, instead saving each render to a file and then stitching it together into a video using FFmpeg. Or just selecting some of my favorite stills and publishing those.

Some supplementary reading:

Setup Fn

I use the following setup function for my sketches,

(fn []
  (q/frame-rate 60)
  (q/color-mode :hsb 360 100 100 1.0)
  
  (q/stroke-cap :project)
  (q/stroke-join :miter)
  (q/stroke-weight 1)

  (q/no-stroke)
  (q/no-fill)
  (q/background 0 0 100)

  ; return a map if using 
  ; fun-mode middleware
  {})

Here Line segment joints and line endings are set to not perform any rounding. With default settings, drawing lines, shapes, and points will cause each call to Quil (and therefore Processing) to do some extra computation. In my experience this “improved” stylization doesn’t really give you much. Additionally, there is a noticeable performance hit when drawing large sets of geometries.

I prefer using the HSB color space. I think it is easier for reasoning about colors artistically. The way it’s setup above, hue is in a range of [0, 360), saturation and lightness are [0, 100]. It becomes very simple to recognize that hue values around 350-50 are red-ish, 120 is green, 150-250 are blue-ish, etc.

Utility Fns, Macros

Context Wrapping

Kindly stolen from @ooesili,

(defmacro with-matrix [& body]
  `(do
     (q/push-matrix)
     (try
       ~@body
       (finally (q/pop-matrix)))))

(defmacro with-style [& body]
  `(do
     (q/push-style)
     (try
       ~@body
       (finally (q/pop-style)))))

The above macros help manage different contexts. Often times I find myself needing to stylize a specific part of a drawing and then revert all those style changes for the rest of the drawing. Maybe I want to add some debug info, in the top right corner. Wrapping that code in with-style, for example, lets me modify any style fields for the duration of the macro’s body. Likewise, with-matrix is useful for executing some drawing elsewhere or scaled, etc. The best part is they can be nested.

Saving Images

(fn [index]
  (let [fname (str "output/temp/" index ".png")]
    (q/save fname)))

The above uses quil.core/save to save images. It will take whatever you see in your sketch and persist it on disk. A few different file types are supported but I use:

  • .tiff for high resolution, and
  • .png for doodles

To stitch images together I use FFmpeg

ffmpeg -i output/temp/%d.png -r 60 -s 1024x1024 -vcodec libx264 -crf 25 -pix_fmt yuv420p output.mp4

The above command sets some sane defaults to keep the output file size low. It’s usually perfect for sharing with folks or uploading to Instagram/youtube. The -r flag will set the framerate and -s will set the output size. I tend to keep this matching my sketch configurations, as seen above.

A Complete Example

(ns com.dedovic.sketch.example
  (:require [quil.core :as q]
            [quil.middleware :as qm])

;; Constants
(def tau (* Math/PI 2))

;; Setup Fn
(defn- setup [] {})

;; Draw Fn, fun-mode
(defn- draw [state] {})

(let [fps 60
      ms-per-frame (/ 1000 fps)]
  (defn- update-state [state]
    (-> state 
        (update :foo inc)
        (assoc :bar (make-bar))
        (assoc :baz (get-baz)))))

(defn make-sketch 
  []
  (let [size [1080 1080]]
    (q/sketch
     :title "Test November"
     :size size
     :renderer :p2d

     :setup setup
     :draw draw
     :update update-state
     :middleware [qm/fun-mode])))

(defn destroy-sketch
 [sketch]
 (.exit sketch))

Interacting with the Example

My typical workflow is light on the REPL (using Lein). An approach is to switch into the sketch’s namespace and run things from there.

In the REPL,

user=> (in-ns 'com.dedovic.sketch.example)
#namespace[com.dedovic.sketch.example]

com.dedovic.sketch.example=> (def sketch (make-sketch))

com.dedovic.sketch.example=> (destory-sketch sketch)

I use vim, and the fireplace.vim plugin is the best way to develop clojure with vim, in my opinion. IntelliJ has good Clojure support too, but it’s not as interactive as vim, and that interactivity is what makes this setup so great for developing art. It’s just creativity with the shortest feedback cycle.

Wrapping Up

Anyway, there’s a lot more I could discuss here. So instead I’ll drop some useful links and leave that for another writeup.

Quil Utilities:

Where to start:

Communities