Draft

Notes on exporting quil sketches

Draft of 2019.04.18

May include: programmingClojure&c.

I like writing Clojure and ClojureScript for the little sketches I produce, and I have some hopes of doing it more often. When what I’m doing is visual, a lot of the time I like to use quil, the ClojureScript wrapper around p5.js.

But the documentation and notes available online are scattered and only approximately findable. So this will be a pile of notes for me to discover, next time. Caveat lector: I work on a Mac, and so I don’t feel the need to help you make your package manager (specifically, your particular crufty old brew installation) work, when all I really need to do is make mine work, so these notes may not suffice for you.

If you have problems with your package manager—whether it’s homebrew or apt or whatever, I encourage you to throw it all away and start fresh. That’s actually the point. A good package manager will tell you when something is missing, and will download dependencies for you on the fly. You should be able to read error messages and reconstruct your “absolutely vital” subset of packages, from up-to-date source code that doesn’t have security vulnerabilities; you shouldn’t try to “fix” your old one, just replace it.


Install ClojureScript and ensure quil works off the shelf. That means:

  • make sure homebrew is up to date and working (run brew doctor to make absolutely sure)
  • make sure Clojure (brew install clojure works) and leiningen (brew install leiningen) are up to date and working
  • lein new quil-cljs myproject
  • cd myproject
  • atom . to launch an editor holding the myproject project folder
  • Back in the terminal, lein figwheel should successfully compile the (default) demo project that has been constructed, and also launch (1) a web server, (2) the figwheel file watcher so that saved changes to [most] files in the project will provoke an immediate recompilation, and most importantly (3) a REPL (but not until you open the project page in a browser). You need the REPL. Yes, most of this stuff can be done by hand, but figwheel simplifies most of the work so much that it’s not worth recording the individual tasks here.
  • navigate (in a web browser) either to the project web page mentioned by figwheel when it launched, or open the local file [myproject]/resources/public/index.html
  • you should be looking at the demo quil project in he browser, with no errors

Change the demo project to do what you want it to do. Whenever you save [most] files, you will trigger an automatic recompilation, and the new version will run in the browser window. Error messages and warnings will also appear in the browser window, in a flash message, so don’t be surprised. Errors will not typically be reported to the REPL.

The demo project uses the defsketch macro, which will be fine unless you want your quil code to communicate with the surrounding web page, for instance if you want to launch it with a button, or pass in arguments from the page form elements. It’s very helpful to use the web page [myproject]/resources/public/index.html not just as a “holder” but to pass in arguments to the sketch, and if that is interesting you won’t easily be able to use the defsketch macro, which assumes a simple isolation.

The structure I have come to prefer (as of this writing) is something like this:

(ns myproject.core
  (:require [quil.core :as q :include-macros true]
            [quil.middleware :as m]))

;; internal functions and sketch-specific definitions here

(defn setup [args]
  ;; stuff happens here
  {:foo 1
   :bar 2} ;; persistent state is this map, returned by this function
   )

(defn update-state [state]
  ;; stuff happens here
  {:foo 2
   :bar 3} ;; returns a new state, based on the old one, in each tick
   )

(defn draw-state [state]
  ;; invoke quil functions that draw the state in the linked container
  )

(defn ^:export doMyThing [args]
  (q/sketch
      :host "sketch-div-id" ;; note that in quil 3.X, a div not a canvas is used
      :size [400 400]
      :setup #(setup args) ;; anon function passing arguments to setup
      :update update-state
      :draw draw-state
      :middleware [m/fun-mode]
      :features [:no-start]
      ))

(doMyThing args) ;; explicitly launches the loop; may not want this

And in the index.html

...
<body>
  <div id="sketch-div-id"></div>
  <script src="js/main.js"></script>

  ... interface here as needed ...

  <script>
  function do_the_thing() {
    var args = // quil args collected from page state here
    myproject.core.doMyThing(args);
  }
</script>

<button onclick="do_the_thing()">Launch!</button>
</body>

Note this is only for development. To handle optimized compilation and export, see below


The demo quil project has two build modes set up in project.clj. The first, "development", is (as of this writing) configured to write non-portable javascript to [myproject]/resources/public/js/main.js:

{:id "development"
   :source-paths ["src"]
   :figwheel true
   :compiler
   {:main "myproject.core"
    :output-to "resources/public/js/main.js"
    :output-dir "resources/public/js/development"
    :asset-path "js/development"}}

The other, "optimized", is for production of a self-contained script, which will include all dependencies. As it stands in the template version, it will also write to [myproject]/resources/public/js/main.js. Personally I don’t want that to happen, so I often change it to write instead to .../js/optimized.js, as shown here.

{:id "optimized"
 :source-paths ["src"]
 :compiler
 {:main "myproject.core"
  :output-to "resources/public/js/optimized.js"
  :output-dir "resources/public/js/optimized"
  :asset-path "js/optimized"
  :optimizations :advanced}}

In the running REPL (which you launched when starting figwheel), I will usually invoke (build-once optimized), which will compile the code and produce the indicated javascript file.

Note that, if you change the optimized file’s name, as I do, you will also need to change the reference to it in the HTML page to match. Otherwise you will still be loading the development version when the page renders.

That is:

...
<body>
  <div id="sketch-div-id"></div>
  <script src="js/optimized.js"></script>
     <!-- change this ^^^^^ to match compiled version -->

  ... interface here as needed ...

  <script>
  function do_the_thing() {
    var args = // quil args collected from page state here
    myproject.core.doMyThing(args);
  }
</script>

<button onclick="do_the_thing()">Launch!</button>
</body>

When you find that suitable, you will find you’ve “exported” the files you need.

However be aware that when you edit project.clj, the figwheel watcher will not recompile your project. That means you will need to do a clean build. This is easy enough using the REPL commands listed by figwheel when it launched, but if you are an impatient sort, you can as easily kill the REPL (ctrl-D) and relaunch it with lein figwheel. That’s heavy-handed, but maybe you didn’t see the helpful suggestions figwheel gave you.

Like me.


To embed the sketch in a separate website, not just the index.html page provided:

  • copy the optimized.js file to the appropriate resource directory of the other site; this can be renamed to suit the project
  • ensure the specified div exists on the page where the sketch needs to be located
  • ensure the specified form elements are present, and are named correctly, if your sketch is at all interactive; if you renamed the javascript file, make sure it’s loaded in the page by name (this can be a problem if you’re cutting-and-pasting code from the quil project into the external one).

Not done yet

These notes do not (yet) include two-way interaction; that is, this passes values from the web form elements into the quil sketch, but not the other way. That will be for another day.

An example would be good about now

ticks: 0

This sketch lets one vary a “selection process” for a simple linear regression, as part of an explanation I’m writing about something else. The details of the wiggling lines aren’t important, just the fact that changing the radio button selection and clicking “restart” will affect the sketch in the appropriate way.

I developed the interface in the quil development environment, obviously avoiding CSS tweaks and stuff that I knew would be provided by the eventual project here. I did rename the javascript file from optimized.js to pareto-fit-1.js in this site’s source tree, simply because I have several dozen such files to juggle, and often want multiples in a single page.

The radio set (note the shared name field, so all the buttons act as a single set in HTML5) loads a simple “value” string into the Javascript variable var m = document.querySelector('input[name="mode"]:checked').value;. That string is passed into the quil function paretofit.core.shimmy(m); intact, and is parsed there to affect the behavior of the sketch.

Note also that any ClojureScript function intended to be called from the surrounding page will need to be labeled with the ^:export metadata flag. Otherwise, the optimized code will rename the function internally (to compress the resulting file), and you won’t be able to call it at all.

Arguably, this is true also for reporting functions your code might expose, for instance if you wanted to display (on the page) a “best value found so far” or a count of ticks executed in the quil sketch on the web page.

As you can see, the example I’ve included does in fact actively rewrite the text on the web page as it runs, by replacing the “ticks” value in the DOM. That’s not at all complicated, but it does introduce some extra work to ensure consistency and to avoid some easy-to-make mistakes (like not having the DOM element to write the value into). I’ll deal with that part later.

Alternately, you may find versions of this same kind of interactive dynamics using a ClojureScript atom in various ways. Maybe we can do that too… next time.