Yesterday night it was too warm to sleep and I have no air conditioning at home, so I thought it was a good opportunity to make some experiments related to the high temperature… “the burning man”
Here’s the algorithm in human language:
- pick up the image from the webcam
- perform a threshold on it to exclude (alpha 0) every pixel below the average skin amount of red (between 44 and 90 depending on the light)
- perform a second threshold to exclude (alpha 0) every pixel above the average skin amount of green and blue (between 3344 and 6677 depending on the light)
- draw the result onto a new bitmapdata with the ADD blendmode
- apply blur to the bitmapdata
- generate a perlin noise of one channel (i chose red)
- displace the bitmapdata towards -y
- apply again a displacement map filter to displace horizontally (i used a cos function to make the x displacement a little likely)
- apply a displacement map towards +y with just a bit less strenght than the previous “-y” one (this is to make the fire propagation much more likely)
- then merge the original webcam output with the displaced one…and you’ve the burning man!
here the actionscript implementation
package { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.BitmapDataChannel; import flash.display.BlendMode; import flash.display.Sprite; import flash.events.Event; import flash.filters.BlurFilter; import flash.filters.DisplacementMapFilter; import flash.filters.DisplacementMapFilterMode; import flash.geom.ColorTransform; import flash.geom.Matrix; import flash.geom.Point; import flash.geom.Rectangle; import flash.media.Camera; import flash.media.Video; [SWF(width="320",height="240",frameRate="31",backgroundColor="0x000000")] public class BurningMan extends Sprite { private var _cam:Camera; private var _vid:Video; private var _fireThreshold:BitmapData; private var _firePerlin:BitmapData; private var _fireOffset:Point; private var _fireSpeed:int = 20; private var _fireDisplaceUP:DisplacementMapFilter; private var _fireDisplaceDWN:DisplacementMapFilter; private var _fireDisplaceSrc:BitmapData; private var _fireBlur:BlurFilter; private var _fireColorTransform:ColorTransform; private var _fireMerge:BitmapData; private var _camBd:BitmapData; private var _b:Bitmap; private static const WIDTH:Number = 320; private static const HEIGHT:Number = 240; private static const ORIGIN:Point = new Point(); private static const REFLECT:Matrix = new Matrix(-1,0,0,1,WIDTH,0); private static const RESIZE_2X:Matrix = new Matrix(2,0,0,2); private var _rect:Rectangle; public function BurningMan() { super(); init(); } private function init():void { _cam = Camera.getCamera(); _cam.setMode(WIDTH,HEIGHT,31); _vid = new Video(WIDTH,HEIGHT); _vid.attachCamera(_cam); _fireOffset = new Point(); _fireThreshold = new BitmapData(WIDTH,HEIGHT,true,0x00000000); _fireMerge = new BitmapData(WIDTH,HEIGHT,true,0x00000000); _firePerlin = new BitmapData(WIDTH*.5,HEIGHT*.5,false,0x000000); _fireDisplaceSrc = new BitmapData(WIDTH,HEIGHT,false,0x000000); _fireDisplaceUP = new DisplacementMapFilter(_fireDisplaceSrc,ORIGIN,BitmapDataChannel.RED,BitmapDataChannel.RED,0,-15,DisplacementMapFilterMode.CLAMP); _fireDisplaceDWN = new DisplacementMapFilter(_fireDisplaceSrc,ORIGIN,BitmapDataChannel.RED,BitmapDataChannel.RED,0,9,DisplacementMapFilterMode.CLAMP); _fireBlur = new BlurFilter(2,2,4); _fireColorTransform = new ColorTransform(1.3,1.1,1,.7); _camBd = new BitmapData(WIDTH,HEIGHT,true,0x00000000); _rect = _camBd.rect; _b = new Bitmap(_camBd); _firePerlin.lock(); _fireMerge.lock(); addChild(_b); addEventListener(Event.ENTER_FRAME,update); } private function update(e:Event=null):void { //locks _camBd.lock(); _fireOffset.y+=_fireSpeed; _firePerlin.perlinNoise(_firePerlin.width*.125,_firePerlin.height*.125,1,0,true,false,BitmapDataChannel.RED,false,[_fireOffset]) _fireDisplaceSrc.draw(_firePerlin,RESIZE_2X); _camBd.draw(_vid,REFLECT); _fireThreshold.setVector(_rect,_camBd.getVector(_rect)); _fireThreshold.threshold(_fireThreshold,_rect,ORIGIN,"<",0xFF440000,0x00000000,0xFF0000,true); _fireThreshold.threshold(_fireThreshold,_rect,ORIGIN,">",0xFF003344,0x00000000,0x00FFFF,true); _fireMerge.draw(_fireThreshold,null,_fireColorTransform,BlendMode.ADD,_rect); _fireDisplaceUP.scaleX = Math.cos(((_fireOffset.y/(2<<3))%360)*180/Math.PI)*2; _fireDisplaceDWN.scaleX = -_fireDisplaceUP.scaleX; _fireMerge.applyFilter(_fireMerge,_fireMerge.rect,ORIGIN,_fireBlur); _fireMerge.applyFilter(_fireMerge,_rect,ORIGIN,_fireDisplaceUP); _fireMerge.applyFilter(_fireMerge,_rect,ORIGIN,_fireDisplaceDWN); _camBd.draw(_fireMerge,null,_fireColorTransform,BlendMode.HARDLIGHT,_rect); //unlocks _camBd.unlock(); } } } |
here’s a preview of myself empowered by the force of the fire!:D

click the photo to see the demo in action