inconvergent

Over the past few weeks I've been trying to put together a small drawing library that will behave in a way that suits my typical workflow. An important feature is that I want to be able to use the library independently of programming language. The current (fairly unstable) version of the library is available at Github.

Generative Image 86d1da6
A generative drawing

I've written about the way I usually draw things in Grains of Sand. But the brief version is that I use randomly sampled numbers to draw primitives (boxes, lines, splines, …). In order to make high resolution prints I need to do this relatively fast. And I want to be able to see the image as it is being drawn. I'm not really aware of any existing libraries or tools that will let me do this, so here I am ...

I've decided to try a model where I have a message queue, and a worker that draws whatever commands (JSON objects) are sent to the queue (Redis). The tool is written in Python, but because of this architecture you will be able to use it from from any environment. As long as you are able to construct JSON, and post the objects to the queue.

The library consists of two parts. The drawing library is called "Desert". Desert handles the drawing, displaying and saving. It uses CUDA to speed up the crucial bits. It's not blazing fast yet, but it seems promising.

The second part is called "Erosion". Erosion consists of the Erosion client and the Erosion worker. You can use the erosion client to send Desert primitives to the message queue. And more importantly, you can use the Erosion worker to process the queue. If you want to use this from your favorite programming language, you will only need to build your own JSON objects and send to the queue.

To give you an idea of what it looks like, here is the Python code used to draw the above image :

with Desert(1000, show=True)\
    .init(fg=black(0.001),
          bg=white()) as c:

  # controls the number of samples
  density = 0.02
  # angles
  a = random(2) * TWOPI
  acceleration = zeros(2)
  noise = 0.00000001
  radius 0.45

  resa = []
  resb = []

  for i in range(2000000):
    a += acceleration
    acceleration += (1-2*random(2))*noise

    # gather points on the circle for angle a[0] and a[1]
    resa.append((cos(a[0]), sin(a[0])))
    resb.append((cos(a[1]), sin(a[1])))

    if not i%50000:
      # construct a desert primitive, and draw it
      # this primitive draws a line with a certain density
      # between points in resa and resb
      c.draw([stroke(0.5 + array(resa)*radius,
                     0.5 + array(resb)*radius,
                     density)]).show()
      resa = []
      resb = []

If you want to send commands to the message queue instead, the code is almost identical, but instead of creating a Desert instance, you create an Erosion instance.


  1. The generative process creating the image will typically be much slower than the actual drawing, so I'm not making any effort to make this real time at the moment.
  2. If posting to Redis is a problem, another alternative is to build a tiny webserver in front.
  3. You can see the JSON format by executing this script.
  4. The complete script is here.
  5. Here is a complete Erosion example.