Fluxus now runs in a browser using WebGL. Not much is working yet – (draw-cube), basic transforms, colours and textures. I’ve also built a small site in django so people can share (or perhaps more likely, corrupt) each other’s scripts. Also much inspired by seeing a load of great live coding at the algoraves by Davide Della Casa and Guy John using livecodelab.

This is a spin off from the work I did a few weeks ago on a silly Scheme to Javascript compiler. It’s still pretty silly, but in order to explain better, first we take a scheme program like this:

;; a tree ( define ( render n ) ( when ( not ( zero? n )) ( translate ( vector 0 1 0 )) ( with-state ( scale ( vector 0.1 1 0.1 )) ( draw-cube )) ( scale ( vector 0.8 0.8 0.8 )) ( with-state ( rotate ( vector 0 0 25 )) ( render ( - n 1 ))) ( with-state ( rotate ( vector 0 0 -25 )) ( render ( - n 1 ))))) ( every-frame ( with-state ( translate ( vector 0 -3 0 )) ( render 8 )))

Then parse it straight into JSON, so lists become Javascript arrays and everything else is a string, also doing minor things like switching “-” to “_”:

[[ "define" ,[ "render" , "n" ], [ "when" ,[ "not" ,[ "zero_q" , "n" ]], [ "translate" ,[ "vector" , "0" , "1" , "0" ]], [ "with_state" , [ "scale" ,[ "vector" , "0.1" , "1" , "0.1" ]], [ "draw_cube" ]], [ "scale" ,[ "vector" , "0.8" , "0.8" , "0.8" ]], [ "with_state" , [ "rotate" ,[ "vector" , "0" , "0" , "25" ]], [ "render" ,[ "-" , "n" , "1" ]]], [ "with_state" , [ "rotate" ,[ "vector" , "0" , "0" , "-25" ]], [ "render" ,[ "-" , "n" , "1" ]]]]], [ "every_frame" , [ "with_state" , [ "translate" ,[ "vector" , "0" , "-3" , "0" ]], [ "render" , "8" ]]]]

Next we do some syntax expansion, so functions become full lambda definitions, and custom fluxus syntax forms like (with-state) get turned into lets and begins wrapped with state (push) and (pop). These transformations are actually written in Scheme (not quite as define-macros yet), and are compiled at an earlier stage. It now starts to increase in size:

[[ "define" , "render" , [ "lambda" ,[ "n" ], [ "when" ,[ "not" ,[ "zero_q" , "n" ]], [ "translate" ,[ "vector" , "0" , "1" , "0" ]], [ "begin" , [ "push" ], [ "let" ,[[ "r" ,[ "begin" , [ "scale" ,[ "vector" , "0.1" , "1" , "0.1" ]], [ "draw_cube" ]]]], [ "pop" ], "r" ]], [ "scale" ,[ "vector" , "0.8" , "0.8" , "0.8" ]], [ "begin" , [ "push" ], [ "let" ,[[ "r" ,[ "begin" , [ "rotate" ,[ "vector" , "0" , "0" , "25" ]], [ "render" ,[ "-" , "n" , "1" ]]]]], [ "pop" ], "r" ]], [ "begin" , [ "push" ], [ "let" ,[[ "r" ,[ "begin" , [ "rotate" ,[ "vector" , "0" , "0" , "-25" ]], [ "render" ,[ "-" , "n" , "1" ]]]]], [ "pop" ], "r" ]]]]], [ "every_frame_impl" , [ "lambda" ,[], [[ "begin" , [ "push" ], [ "let" ,[[ "r" ,[ "begin" , [ "translate" ,[ "vector" , "0" , "-3" , "0" ]], [ "render" , "8" ]]]], [ "pop" ], "r" ]]]]]

Then, finally, we convert this into a bunch of Javascript closures. It’s pretty hard to unpick what’s going on at this point, I’m sure there is quite a bit of optimisation possible, though it does seem to work quite well:

var render = function (n) { if ( ! (zero_q(n))) { return ( function () { translate(vector( 0 , 1 , 0 )); ( function () { push() return ( function (r) { pop() return r }(( function () { scale(vector( 0.1 , 1 , 0.1 )) return draw_cube() })()))})(); scale(vector( 0.8 , 0.8 , 0.8 )); ( function () { push() return ( function (r) { pop() return r }(( function () { rotate(vector( 0 , 0 , 25 )) return render((n - 1 )) })()))})() return ( function () { push() return ( function (r) { pop() return r }(( function () { rotate(vector( 0 , 0 , -25 )) return render((n - 1 )) })()))})()})()}}; every_frame_impl( function () { return ( function () { push() return ( function (r) { pop() return r }(( function () { translate(vector( 0 , -3 , 0 )) return render( 8 ) })()))})()})

Then all that’s needed are definitions for all the fluxus 3D graphics calls – the great thing is that these are also written in Scheme, right down to the low level WebGL stuff, so the only Javascript code needed is half of the compiler (eventually this also can be replaced). I was quite surprised at how easy this is, although it is greatly helped by the similarity of the two languages.