meemoo hackable web apps

An introduction to noflo-canvas

16 Jun 2014 by Vilson Vieira

During the last weeks we were working on noflo-canvas and experimenting with it. Here I describe some of those developments and how we ended with the current noflo-canvas design.

Arrays and grids

Following Forrest’s suggestions we have designed noflo-canvas inspired by Grasshopper, an algorithmic modeling tool for Rhino. In Grasshopper you can create an unique point or an array of points using the same component. In noflo-canvas, you have components like MakePoint which receives an unique pair of (x,y) coordinates or an array of them.

When given an array of x and y coordinates, MakePoint makes one point for each pair of x and y. It is like Spreads from VVVV.

Another possibility is to create one point for each possible combination of x and y. MakeGrid does that and resembles the cross product between two vectors (also resembles Cross from VVVV). This example makes it easy to understand:

Such patterns are powerful to algorithmic design because we can create complex shapes using few components. Imagine how many MakePoints are necessary to reproduce the same of just one MakeGrid.

Lazy evaluation

Another interesting detail in noflo-canvas design is how drawing commands are evaluated. Draw is the main component in noflo-canvas and all the magic happens inside it. Take the following program as an example:

Example program

Draw receives these commands:

    [{"type": "fill", "items":
         {"type": "circle", "center":
             {"type": "point", "x": 100, "y": 100}, 
             "radius": 100},
         "fillstyle":"#00ff00"}] 

If we remove JSON stuff we get this Lispy representation:

    (fill (circle (point 100 100)
                  100)
          "#00ff00")

Lisp was a great inspiration not only for how we represent commands but on how we evaluate them. Draw parses received commands and applies the respective Canvas 2d method on its arguments.

The entire process resembles a lazy evaluator: each component just has to generate a specific instruction like {"type": "point", "x": 100, "y": 100} and send it to the target component. It repeats until those commands are finally received by Draw that evalutes them.

Connection ordering is important in those situations because we want commands evaluated in specific order: “I want to clean the canvas first, then draw a circle, then …”. NoFlo supports connection ordering which makes this design possible.

This lazy evaluation pattern is becoming useful in FBP, specially when you have to deal with an independent dataflow like the ones of Canvas 2d or Web Audio.

Running on backend

Thanks to node-canvas, it is also possible to use noflo-canvas on NoFlo programs running on Node.js. Flowhub has helpful documentation about how to connect Flowhub on your Node.js runtime.

Node-canvas uses Cairo as its graphics toolkit and we are using it to generate images with high dimensions on backend. Ready-to-use images like that are useful for caching. We are currently reporting experiments of this use of noflo-canvas in this issue.