Skip to content

cassiel/clojure4node4max

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

89 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

-*- mode: org; mode: visual-line; -*-

Clojure for Node for Max

Introduction

This is a simple project for bringing up a ClojureScript network REPL to talk to Max 8 via a Node.js sub-process. This means that Max can be live-coded in ClojureScript, at least to the extent that the node.script instance in Max is connected to interesting things in the patcher (via inlets, outlets, named dictionaries). We describe the support for CIDER inside Emacs, although other IDE toolchains are available and should work.

Background

Max has a library object called node.script which can fire up a Node.js process and attach it to the enclosing patcher document, so that the patcher can run Javascript code, and the Javascript code can manipulate things in the Max world.

Figwheel Main is a set of libraries which equips a Javascript client (a web browser, or Node.js) with a network connection to a Figwheel server; the latter runs in Clojure (on the JVM), hosts the ClojureScript cross-compiler, and supports a REPL (read-eval-print loop). When everything’s running, ClojureScript can be live-coded via the server, which compiles everything to Javascript and sends it through to the client. The server also tracks changes to source files, uploading code to the client as required.

When everything is running, the setup looks like this:

  • Max with an instance of node.script and a Node.js process attached to it
  • A Figwheel Main server, which Max’s Node.js attaches to
  • Emacs (or equivalent), attached to the server for live coding via CIDER

Most of this machinery is invisible (so in particular, the Max application code in ClojureScript for Max needs to know nothing about live coding support); Figwheel and CIDER augment the code automatically to tie everything together.

Setup

We were initially using the basic (but new) Clojure CLI tools, but a build on a new machine caused some breakage, so we’ve switched back to tried-and-trusted Leiningen. The Figwheel Main site has good documentation, though that is oriented towards Clojure CLI.

Compile and Run: First Shot (simple)

A very simple setup with a network REPL. In all the simple examples, the actual code is just sitting in scratch.cljs. Below is the build configuration file, dev.cljs.edn:

^{:watch-dirs   ["src"]
  :node-command "node"
  :launch-node  false}
{:target :nodejs
 :main   simple.core}

The target is :nodejs. Also, in the meta-data, we’ve disabled :launch-node since that’s done by Max. For testing (without the Max API) make this true, in which case the value of :node-command might need to be altered.

Note the symbolic link from simple.js to the actual built target. This is needed because Max’s node.script object doesn’t support complex file paths.

To build, invoke cider-jack-in-cljs from Emacs while visiting scratch.cljs. For ClojureScript REPL type, enter figwheel-main. For figwheel-main build, enter :dev.

Start the Max node process once the CIDER session has initialised and is waiting for a client connection.

Working with Promises (promises)

A more sophisticated example, interacting with Max dictionaries. Since dictionary operations return Javascript promises, we’re using cljs-promises, which wraps them in ClojureScript’s core.async machinery. (TODO: that package is probably not needed any more, use <p! instead.)

We can use NPM to wheel in WebSocket support for the REPL to replace the less efficient polling: there’s a message box to mash to do that in the Max patcher. (TODO: that, or something equivalent, might be available in CLJSJS, which would be neater.)

OSC (net.cassiel.osc)

Quite a complex example for bringing up an OSC forwarder with logging into a Max dictionary (the OSC being handled internally in Node). The complexity is the bootstrapping of the components, since we want to read some configuration (addresses, ports) from a Max dictionary before we kick off - and that’s a deferred, promise-based call as well.

Features: forwards all incoming OSC to an output address and port (which can be manually edited in config.json), while logging the data to a dictionary. The dictionary is keyed by OSC address, and the first argument is a running count of the number of occurrences of the address, followed by the actual arguments of the last-seen message.

(The cljs-promises package is obsolete; there’s now first-class support for promises in core.async.)

Blofeld (net.cassiel.blofeld)

This example has been moved to its own repository.

About

ClojureScript for Node for Max

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published