ClojureScript

Gary Fredericks edited this page Jun 24, 2015 · 72 revisions

Quick start

Use quil-cljs Leiningen template to create a project from scratch:

lein new quil-cljs hello-quil

In src directory you have ClojureScript with an example of a sketch.

Processing.js library is used as rendering backend for Quil on ClojureScript. To run sketch run lein compile and then open ./index.html.

Useful links

If you haven't used ClojureScript before check following links:

  1. ClojureScript: https://github.com/clojure/clojurescript
  2. cljsbuild Leiningen plugin: https://github.com/emezeske/lein-cljsbuild

And keep in the mind that ClojureScript code is translated to JavaScript outside of the browser.

We try to keep maximal compatibility between Clojure and ClojureScript versions of Quil, but it's not complete. You can see a list of supported (in ClojureScript version) functions on this page: https://github.com/quil/quil/wiki/List-of-available-functions-in-ClojureScript

Usage details

defsketch macro

The main thing in Quil is defsketch macro. Therefore consider the main differences in this macro between Clojure and ClojureScript versions.

The key point is that we have to assign sketch to a canvas element on a html page. This canvas element is specified using id field. This means that ClojureScript sketch has to "know" about the id. Use :host property for that:

(q/defsketch my-sketch-definition
  :host "canvas-id"
  :draw draw
  :size [300 300])

somewhere on page:

<canvas id="canvas-id"></canvas>

If the :host property is not defined then Quil uses sketch name as id. In previous example sketch name was my-sketch-definition, so if we omitted :host property Quil would look for canvas with id my-sketch-definition.

Currently you can use the following properties for defsketch macro in ClojureScript:

  1. :host
  2. :setup
  3. :draw
  4. :size
  5. :renderer (supported :p2d and :p3d modes, :p2d is default)
  6. :key-pressed, :key-released, :key-typed
  7. :mouse-clicked, :mouse-dragged, :mouse-moved, :mouse-pressed, :mouse-released, :mouse-exited, :mouse-entered
  8. :middleware with middlewares fun-mode and navigation_3d
  9. :features with one option :no-start. By default sketch started when html page is loaded. But if you set sketch option :no-start then sketch not initialized when html page loaded.

with-sketch macro

To execute arbitrary code in sketch environment you can use with-sketch macro. Code, wrapped in with-sketch, can call any Quil functions. For example:

(defn ^:export clean-sketch []
  (q/with-sketch (q/get-sketch-by-id host)
    (q/background 255)))

with-sketch macro is defined only for ClojureScript Quil code, and might be ported to regular Quil later. It's useful for controlling sketch outside sketch code.

ClojureScript limitations

ClojureScript has a few important limitations:

  1. You can't use :refer :all or (:use some-ns-name) in ns form. Only (:require [some-ns :only [refers-list]]) or (:require [some-ns :as ns-alias]) are available;
  2. You must use (:require-macros [some-ns :only [imported-macros-list]]) in ns from;
  3. You can't define macros in cljs files. Macros can be defined only in clj files.

If you have not worked with ClojureScript before carefully read the links at the beginning of the page.

How to translate regular Quil sketch to ClojureScript

  1. Create an html page with canvas element with specified id field.
  2. Create a copy of sketch clj file with cljs extension.
  3. Add :include-macros true to (:require [quil.core :as q]): (:require [quil.core :as q :include-macros true]).
  4. Add :host parameter to q/defsketch.
  5. Add ClojureScript to :dependencies in project.clj.
  6. Add CljsBuild plugin to :plugins in project.clj.
  7. Add leiningen.cljsbuild to :hooks in project.clj.
  8. Set :cljsbuild parameter in project.clj.
  9. Include processing-1.4.8.js and ClojureScript output js scripts in the html page.
  10. Run lein compile and open the html page in browser to see your sketch.

ClojureScript and Clojure versions in project.clj

ClojureScript 0.0-2261 depends on Clojure 1.6.0, so you can't use older Clojure version, like 1.5.1.

This means that you must use ClojureScript 0.0-2261+ and Clojure 1.6.0+ or ClojureScript 0.0-2234 or less and Clojure 1.5.1 (we don't test Quil for previous versions of Clojure, so it might work or might not).

Advanced compilation mode

Note: this is no longer applies for Quil 2.2.5 as all externs and preamble are automatically applied without you need to do anything, just change optimization mode to :advanced.

For quil 2.2.4 and below. Advanced compilation mode drastically reduces size of compiled js by renaming all functions and stripping out unused code. To compile Quil in advanced mode do following:

  • use :optimizations :advanced;
  • disable pretty printing by specifying: :pretty-print false;
  • specify processing.js externs file: :externs ["externs/processing.js"];
  • use minified version of processing.js: :preamble ["processing.min.js"];

Example:

:cljsbuild
{:builds [{:source-paths ["src"]
           :compiler
          {:output-to "web/js/main.js"
           :externs ["externs/processing.js"]
           :preamble ["processing.min.js"]
           :optimizations :advanced
           :pretty-print false}}]})

More info about ClojureScript and advanced mode: http://lukevanderhart.com/2011/09/30/using-javascript-and-clojurescript.html.

Ways to run sketch

Simple

You can create project with Leiningen template and use autostart feature. Look at the example:

HTML page:

<html>
<head>
  <title>hello</title>
  <script type="text/javascript" src="js/processing-1.4.8.js"></script>
  <script type="text/javascript" src="js/main.js"></script>
</head>
<body>
  <canvas id="hello"></canvas>
</body>
</html>

Sketch code:

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

(defn draw []
  (q/background 255)
  (q/fill 0)
  (q/ellipse 56 46 55 55))

(q/defsketch hello
  :draw draw
  :host "hello"
  :size [300 300])

Now run lein compile and open you html page in browser. And sketch automatically starts when html page loaded.

You can use it in the development process or in you fully developed sites.

Advanced

This method based on :features [:no-start] defsketch option.

Suppose you have the following code:

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

(defn draw []
  (q/background 255)
  (q/fill 0)
  (q/ellipse 56 46 55 55))

(q/defsketch hello
  :draw draw
  :host "hello"
  :features [:no-start]
  :size [300 300])

To run this sketch call (hello.core/hello) from ClojureScript or hello.core.hello() from JavaScript. Look at the following code:

<html>
<head>
  <title>hello</title>
  <script type="text/javascript" src="js/processing-1.4.8.js"></script>
  <script type="text/javascript" src="js/main.js"></script>
</head>
<body>
  <canvas id="hello"></canvas>
  <button onclick="hello.core.hello()">Run sketch!</button>
</body>
</html>

Sketch starts on button click.

You can use this approach (for example) for embedding sketches to some blog platforms (which allow you to edit raw html of a post).

Manual

If you want to have all control over you sketches you should use quil.core/sketch function to create a sketch. Look at the simple example:

(ns hello.core
  (:require [quil.core :as q]))

(defn draw []
  (q/background 255)
  (q/fill 0)
  (q/ellipse 56 46 55 55))

(defn hello []
  (q/sketch
    :draw draw
    :host "hello"
    :size [300 300]))

You can call hello.core.hello from ClojureScript or JavaScript to run the sketch.

quil.core/sketch support all parameters of defsketch. But you must specify :host parameter for quil.core/sketch unlike defsketch macro.

This approach should be used if you want to change the initialization order.