ClojureScript for Max

cljs-square-440x440

I’ve been writing graphics widgets in Javascript for MaxMSP using JSUI for a few years (things like the TextBrick and the Nixie tube display, as well as the Forbidden Planet-inspired Krell Mixer Panel which I really must release some time), and I’ve also been working in Clojure for a while, so the arrival of ClojureScript, targetting Javascript as its back end, prompted an investigation of using Clojure to develop user interface panels and scripting extensions for Max without writing any Javascript directly.

Summary: despite a few niggles it all hangs together rather nicely, but be wary of thinking in Javascript terms when coding in ClojureScript, especially when porting example code, otherwise it can all go a bit pear-shaped.

Technical summary: ClojureScript is used as a batch compiler to turn a tree of Clojure source files into a single Javascript program as output. Part of this process involves passing Javascript through the Google Closure compiler, optimising it into a faster, but generally unreadable, form. If the original Javascript makes reference to external libraries (or is required to export names into other code), then there are bits of configuration to prevent the Google compiler from transforming the Javascript so much that the imported or exported names no longer match the resulting code.

Code: everything is here on GitHub. There’s a handful of documented, worked examples and a Max 6 project you can launch to try everything out. As always, the wonderful Leiningen is used as the build tool.

Now for the niggles. The only real headache at this stage is the Google compiler: when running in full optimisation mode, it generates invalid Javascript for some of the examples. Since the output is heavily transformed it’s very difficult to determine the cause, and the ClojureScript tool chain doesn’t allow the compiler’s various debugging flags to be activated, but since the fault is always on the same line of output, and near the top of the output file, I’m guessing that it’s part of the base Clojure support. More later, when I get a chance to investigate properly.

The second niggle is wonderfully didactical: porting one of Darwin Grosse‘s interactive drawing examples (“Puffdraw”, from day 6 of the November Patch-a-day) resulted in a program which seemed to work, but constantly ran out of heap, or caused the garbage collector to blow the stack. Here are a couple of pertinent bits of the code:

This thing gets called rapidly to track mouse pointer movements and is also on a 30-millisecond timer; on each event a new point is added to the front of a queue, the last point is taken off the end, and the queue is redrawn on screen. (The code now uses take instead of drop-last, but the result is the same.)

The drawing routine looks something like this:

This is a fairly straight transcription from the Javascript original: a for-loop over the queue, indexing into it to pick out the (X, Y) points to plot.

So, why does this blow up? (Let’s ignore the fact that the iteration makes the traversal complexity O(n^2) on the length.) Well, Clojure is a lazy language, so the queue generation (cons, drop-last) builds suspended data structures. Because the for-loop knows how long the queue is, it never attempts to read the end of it and force its complete evaluation. Result: each time the queue gets cycled with new data, a bigger suspended data structure is built, and it is never pared down by being examined to the end.

The fix: do a proper sequence iteration along the queue. (See the documented code for details.) And the moral of this tale: be very wary of adopting a non-idiomatic programming style in a lazy language. Just because it’s simple and works in something eager and imperative like Javascript doesn’t mean it won’t go off its trolley in Clojure.

Comments

5 Comments so far. Leave a comment below.
  1. I use clojurescript when building website. But when dealing with Max scripting, I prefer coffeescript because its compiler is fast and lightweight. So I can make frequent changes and get feedback immediately.

  2. I need to play with Coffeescript at some stage – it does look neat – but I’m pretty comfortable in Clojure already, and even though it generates thousands of lines of output, compilation time (non-optimised) averages about 2 seconds and Jägermonkey loads code pretty quickly, so the autoload turnaround is still pretty fast.

  3. Hi in trying to run the projects/jsui-cljs/patchers I get this error from the max console complaining about a missing _main.js file.

    Do you have any idea what this might be?

    btw : I really like where you seem to heading with this concept of integrating clojure / clojurescript and max. Your proof of concept of clojure objects in max, opened my eyes to what can be done in max.

    Keep up the good work!

  4. …and two years later, doing a lot more with CoffeeScript, which I like a lot. I think it really needs to be combined with something like Mori or Immutable JS to avoid the usual aliasing/coupling problems of conventional languages.

Leave a Reply to cassiel Cancel reply

Disclaimer
Your email is never published nor shared.
Tips

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <ol> <ul> <li> <strong>

Ready?
Required
Required