I’m a huge fan of working with BitmapData inside Flash. It’s remarkably powerful and can be used to create a huge and varied amount of work. Perhaps you’ve seen Erik Natzke’s brilliant work, or Grant Skinner’s Interactive Elm Tree experiment, and have been wondering how you can use Flash as a viable artistic medium?
One approach to this question is to redraw an image based on certain algorithms, much as Photoshop would treat an image via a filter. Flash makes it easy to read pixel data and manipulate images. Circle Art allows the user to ‘paint’ with a magic brush that is affected by not just the mouse’s position but also the pixels under and around the cursor. For example, Circle Art can filter this source image:
or this:
or this:
Some video examples of Circle Art:
Or, with a slightly different pastel-like render loop:
The way this works is actually quite simple. At it’s core, Circle Art is responsible for reading a pixel and examining pixels in four directions (up, down, left, right) until the examined pixel’s color exceeds a given tolerance from the source pixel’s color. Once all four points have been found, we draw a circle centered inbetween the four points and color it the source pixel’s color. That is, essentially, the bulk of it, and what this means is that areas of the source image that have smoother colors will yield larger circles, and the areas of higher contrasting colors will produce smaller circles.
There’s a bit more magic going on, such as adding randomness to the source pixel’s x and y location so that if the user isn’t moving their mouse they’ll still be creating new circles, and a little blend mode action to excite the output image.
The fun part is playing with all the parameters to see how they affect the output. The pastel video above is only a few variables away from the other video, yet the effect appears nearly entirely different.
You can play with the demo here and download the source here.
And now, an explanation of the CircleArt class:
Most of the class should be fairly straightforward. We attach a bitmap as a source image, and set an event listener up to begin drawing circles at the mouseX and mouseY positions until the mouse is released. There are a few little hooks in there, such as the variable _loopCount which tell flash to draw more than one circle per loop, which makes the effect populate much faster.
The drawCircle method does the bulk of the work. First, pick a pixel ‘close’ to where the mouse is located:
private function drawCircle( xm:uint, ym:uint ) : void {
var x:Number = Math.min( _sourceBD.width, Math.max(0, xm + Math.random() * _randomizer - Math.random() * _randomizer ) );
var y:Number = Math.min( _sourceBD.height, Math.max(0, ym + Math.random() * _randomizer - Math.random() * _randomizer ) );
Next, get the color of the pixel and set up our local variables:
var col:Number = _sourceBD.getPixel(x, y); var bounds:Rectangle = new Rectangle(x, y, _spacing, _spacing); var i:uint; var col2:uint var w:uint = _sourceBD.width; var h:uint = _sourceBD.height; var minX:uint = x - _spread; var minY:uint = y - _spread; var maxX:uint = x + _spread; var maxY:uint = y + _spread;
Now look in each direction (left, up, down, and right) up to the min and max values or until we encounter a pixel that has a greater color difference between the source pixel than our _threshold value. We store the values of each location by means of a rectangle.
//left
for (i = x - _jumpRate; i > minX; i -= _jumpRate) {
col2 = _sourceBD.getPixel(i, y);
if ( discreetColors( col, col2, _tolerance ) ) {
bounds.left = i;
break;
}
}
//right
for (i = x + _jumpRate; i minY; i -= _jumpRate) {
col2 = _sourceBD.getPixel(x, i);
if ( discreetColors( col, col2, _tolerance ) ) {
bounds.top = i;
break;
}
}
//bottom
for (i = y + _jumpRate; i < maxY; i += _jumpRate) {
col2 = _sourceBD.getPixel(x, i);
if ( discreetColors( col, col2, _tolerance ) ) {
bounds.bottom = Math.max(_spacing, i);
break;
}
}
Now we’ll determine the area in which to draw our circle. Our circle will always be symmetrical (although interesting experiments occur when you draw different shapes), so we’ll pick the smaller of the rectangle’s width and height, but always ensure it’s larger than our _spacing variable, as sometimes Flash would be drawing a circle of zero radius.
var width:Number = bounds.width; var height:Number = bounds.height; var scale:Number = Math.max( _spacing, Math.min( width, height ) ); var halfSize:uint = Math.max(2, scale / 2); _circle.graphics.clear(); _circle.graphics.lineStyle(0, col, .9 ); _circle.graphics.drawCircle(0, 0, halfSize);
Finally, we’re ready to position our circle and draw it. Experimenting with difference BlendModes yields interesting results, as does adding filters to the circle, or altering the drawing order. In short, you can create many many different effects in these few lines of code:
var mtx:Matrix = new Matrix(); mtx.translate( bounds.left + bounds.width / 2, bounds.top + bounds.height / 2 ); _circle.filters = []; // take a 'snapshot' _canvasBD.draw( _circle, mtx, null, BlendMode.NORMAL ); _blurFilter.blurX = scale; _blurFilter.blurY = scale; _circle.filters = [_blurFilter]; // draws on FIRE _canvasBD.draw( _circle, mtx, null, BlendMode.ADD ); }
And there you have it! I’d love to see what you come up with. The next steps for me are allowing the user to upload any image, as well as capturing the mouse movements so we can play back an ‘animation’ of the user’s mouse movements. I’d also like to take a note from Erik Natzke’s book and output large images that, perhaps one day, could be printed onto canvas.








3 comments
Comments feed for this article
October 22, 2009 at 7:43 am
Evan Squire
Very cool work I look forward to playing around with it when i have the time, any cool new bitmapData experiments in the year since you wrote this?
Also I wanted to throw Joshua Davis onto your list of generative artists, he has some pretty amazing work.
Evan
February 3, 2010 at 7:41 pm
Chris
for some reason this .zip file is corrupt after download… is the file missing? I’d like to try this with objects tweened through the stage and not just tied to mouse movement.
February 5, 2010 at 9:36 pm
Dru Kepple
@Chris: sorry about the corrupt .zip. Unfortunately, Lucas, who originally posted this, has moved on to bigger and better things, so all that’s left of this is that .zip. I did, however, download the zip and unzipped it successfully. So, I’m putting back up a re-zipped version of the file (here). Now, Lucas would have zipped in the same way that I just did, which is through the Mac OS X Finder’s context menu. If you continue to have problems, just let us know.
As a backup plan, I’ve posted the individual files, uncompressed, here:
http://www.summitprojects.com/labs/circleArt/src/CircleArt.as
http://www.summitprojects.com/labs/circleArt/src/circleArt.2.0.1.fla
It’ll be up to you get the class file into a package folder and working, but in case the zip fails again, we have a backup.