Living Code

Tags: Art Generative Voodoo

Sep 16, 2010 • 3 min read

I saw this tweet the other day and it got me thinking about a project I worked on a few months back and how I could bring it to the web.

@Nerdmeritbadges Now I’m fascinated by voodoo veves: http://cot.ag/c9kU1g Hmm, What’s the veve for emacs? ^JY

At the beginning of the summer, Daniela and I worked on some visual poems for a contest, one of which was a self-portrait with my face overlaid with generative line art. In part the line art was inspired by Voodoo veves, and used the same page mentioned in the above tweet to refresh my memory of them. My interpretations was pretty fast and loose, I’m afraid, and the results were not much like those hand-made veves of flour or chalk, but I was happy enough with the result that I’ve been using it as my avatar around the web.

I wrote the code for my visual poems in NodeBox, but I’ve ported the veve() function to Javascript, using the Raphaël library to create SVG (or in IE, VML) graphics. If you reload the page, you will get a new “veve” each time. The code itself is fairly simple. We define some utility functions, then call the recursive function veve().

``````// Initialize Raphael based on the div with id="veve"
var paper = Raphael("veve", 500, 500);

// Initialize a list of 8 directions to re-use
var dA = Math.PI / 4;
var directions = [];
for (var a = 0; a < 8; a++){
directions.push(dA * a);
}

var LINE_MULT = 20;

function removeItem(list, item){
list.splice(list.indexOf(item), 1);
}

function random(a,b){
// 'Returns an integer between a and b, inclusive';
// 'If b is not specified, returns an integer between 0 and a';
if (b === undefined){
b = a;
a = 0;
}
return Math.floor(Math.random() * (b-a + 1)) + a;
}

function choice(list){
// This is an exclusive, or mutating choice that
// picks a random item from a list and removes that
// item before returning it
var idx = random(0, list.length - 1);
var item = list[idx];
list.splice(idx, 1); // remove item from list
return item;
}

// Find starting point outside of the circle
return {
x: centre_pt.x + Math.cos(direction) * radius,
y: centre_pt.y + Math.sin(direction) * radius
};
}

function end(start_pt, direction, units){
// Find end point just outside next circle
return {
x: start_pt.x + Math.cos(direction) * (units * LINE_MULT),
y: start_pt.y + Math.sin(direction) * (units * LINE_MULT)
};
}

// Find point in center of next circle
return {
x: end_pt.x + Math.cos(direction) * radius,
y: end_pt.y + Math.sin(direction) * radius
};
}

function line(pt1, pt2){
// Draw a line and style it
var l = paper.path('M' + pt1.x + ' ' + pt1.y + 'L' + pt2.x + ' ' + pt2.y);
l.attr({'stroke-width': random(1,4), 'stroke': 'rgba(0,0,0,30)'});
}

function veve(centre_pt, n, return_path){
var i, radius, dirs, direction, start_pt, end_pt;
if (n < 2) return;
dirs = directions.slice(); // make a local copy
// Don't go back the way we came
removeItem(dirs, (return_path + 4) % 8);
for (i = 0; i < n; i++){
direction = choice(dirs);
end_pt = end(start_pt, direction, n);
line(start_pt, end_pt);
veve(next(end_pt, direction, (n-1) * RADIUS_MULT), n-1, direction);
}
}

var c, i;
for (i = 0; i < count; i++){
c.attr({'stroke-width': random(1,4), 'stroke': 'rgba(0,0,0,30)'}); 