In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

Scary Font Effect

//ちょっと不気味な感じ
//http://linktale.net/

// forked from fladdict's FITC Cool Japan side A / Particle simple 40000
/**
* 
* Sample code for FITC
* Basic 40000 particle.
* What is interesting is that, actually this one is 4 times more amount of particles than that of smokey one.
* However people feel this one use less particle.
* 
*/
package {
    import flash.display.Sprite;    
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.BlendMode;
    import flash.events.Event;
    import flash.events.TimerEvent;
    import flash.filters.BlurFilter;
    import flash.geom.ColorTransform;
    import flash.geom.Matrix;
    import flash.geom.Point;
    import flash.media.Sound;
    import flash.media.SoundTransform;
    import flash.net.URLRequest;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;
    import flash.text.TextFormatAlign;
    import flash.utils.ByteArray;
    import flash.utils.Timer;
    
    public class ScaryFontEffect extends Sprite {
       //View
        protected var canvas:BitmapData;
        protected var canvasBitmap:Bitmap;
        
        //BitmapData that contains the actual froce that is applied to particles.
        protected var forceMap:BitmapData;
        protected var maskMap:BitmapData;
        
        //BitmapData that contains the force of the storm.
        protected var stormMap0:BitmapData;
        //protected var stormMap1:BitmapData;
        
        //BitmapData that contains the mouse repulsion force of the stage.
        protected var repulsionMap:BitmapData;
        protected var repulsionHistoryMap:BitmapData;
        protected var repulsionFadeMap:BitmapData;
        
        //the number of particles. 5000-200000
        protected var particleNum:int = 40000;
        protected var particles:Vector.<Particle>
        
        //
        protected var stormScaleRatioX:Number;
        protected var stormScaleRatioY:Number;
        protected var stormCycle:Number = 0;
        
        //color transform that guradually changes canvas.
        protected var fadeCanvasColt:ColorTransform;
        protected var canvasBlurFilter:BlurFilter;
        
        //temporaly variables
        protected var tempColt:ColorTransform = new ColorTransform();
        protected var tempPt:Point = new Point();
        protected var tempMat:Matrix = new Matrix();
        
        protected var txt:String = "つれづれなるままに日暮らし硯にむかひて心にうつりゆくよしなし事をそこはかとなく書きつくればあやしうこそものぐるほしけれ";
        protected var txtNum:uint = 1;
        protected var txtFormat:TextFormat = new TextFormat("_明朝", 120, 0xffffff, true, null, null, null, null, TextFormatAlign.LEFT);
        protected var sound:Sound;
        protected var trans:SoundTransform;
        
        public function ScaryFontEffect() {
            //Wonderfl.capture_delay(10);
            Wonderfl.disable_capture();
            
            init();
            reset();
        }
        
        protected function init():void
        {
            //sound
            sound = new Sound(new URLRequest("http://linkalink.jp/enok/wonderfl/sound/s1.mp3"));
            trans= new SoundTransform();
            trans.volume = 0.5;
            
            //build View
            canvasBitmap = new Bitmap(null, "auto", false);
            addChild(canvasBitmap);
            
            //initialize the map that contains force of the storm;
            stormMap0 = new BitmapData(256,256,false,0x000000);
            stormMap0.perlinNoise(128,128,3,Math.random()*100,false,true,3,false);
            //stormMap1 = new BitmapData(256,256,false,0x000000);
            //stormMap1.perlinNoise(128,128,3,Math.random()*100,false,true,3,false);
            
            //
            forceMap = new BitmapData(256, 256, false, 0x000000);
            
            //mask
            var textField:TextField = new TextField();
            textField.defaultTextFormat = txtFormat;
            textField.text = txt.substr(0, 1);
            textField.autoSize = TextFieldAutoSize.LEFT;
            maskMap = new BitmapData(256, 256, true, 0x000000);
            var matrix:Matrix = new Matrix();
            matrix.translate((256-textField.width*2)/2*stormScaleRatioX, 0);
            matrix.scale(2.0, 2.0);
            maskMap.draw(textField, matrix);
            
            //initialize repulsionMap
            repulsionHistoryMap = new BitmapData(256,256,false,0x000000);
            repulsionFadeMap = new BitmapData(256,256,true,0x20808000);
            repulsionMap = new BitmapData(256,256,false,0);
            
            /**
            * First we preculculate the repulsion force field that mouse cursor generates as a bitmapdata. 
            * Red channel of bitmap data contains horizontal force.
            * Green channel of bitmap data contains vertical force. 
            */
            var dx:int, dy:int;
            var dist:Number, fx:Number, fy:Number;
            var col:int;
            for(var yy:int=0; yy<256; yy++)
            {
                for(var xx:int=0; xx<256; xx++){
                    dx = xx-128;
                    dy = yy-128;
                    dist = Math.sqrt(dx*dx+dy*dy);
                    if(dist<1){
                        fx = 0;
                        fy = 0;
                    }else{
                        fx = dx / dist / dist / dist * 10000;
                        fy = dy / dist / dist / dist * 10000;
                    }
                    fx = (fx<-128)? -128 : (fx>127)? 127 : fx;
                    fy = (fy<-128)? -128 : (fy>127)? 127 : fy;
                    fx += 128;
                    fy += 128;
                    col = (fx<<16) + (fy<<8)
                    repulsionMap.setPixel(xx,yy,col);
                }
            }
            //You can see 
            canvasBitmap.bitmapData = repulsionMap;
            
            //initialize particle
            particles = new Vector.<Particle>(particleNum);
            
            var particle:Particle;
            var rr:int;
            for(var i:int=0; i<particleNum; i++)
            {
                particle = new Particle();
                particle.x = Math.random() * stage.stageWidth;;
                particle.y = Math.random() * stage.stageHeight;
                
                //Customize the range of the particle color according to the amount of the particle and stage size.
                particle.r = particle.g = particle.b = Math.min(Math.random()*16+48,255);  
                
                //Customize the rage of the scaleFactor according to the amount of the particle and stage size.
                particle.scaleFactor = (Math.random()*0.1 + 0.95) * 0.0003;
                particles[i] = particle;
            }
            
            var val:int = -32;
            fadeCanvasColt = new ColorTransform(1,1,1,1,val,val,val,0);
            //canvasBlurFilter = new BlurFilter(2,2,3);
            
            //set event listeners
            stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
            stage.addEventListener(Event.RESIZE, resizeHandler);
            
            var timer:Timer = new Timer(2000, 0);
            timer.addEventListener(TimerEvent.TIMER, onTimer);
            timer.start();
            
            sound.play(0,1,trans);
        }
        
        private function onTimer(e:TimerEvent):void {
            sound.play(0, 1, trans);
            
            var particle:Particle;
            for(var i:int=0; i<particleNum; i++){
                particle = particles[i];
                particle.x = Math.random() * stage.stageWidth;;
                particle.y = Math.random() * stage.stageHeight;
                particle.vx = particle.vy = 0;
            }
            
            var textField:TextField = new TextField();
            textField.defaultTextFormat = txtFormat;
            textField.text = txt.substr(txtNum, 1);
            textField.autoSize = TextFieldAutoSize.LEFT;
            maskMap = new BitmapData(256, 256, true, 0x000000);
            var matrix:Matrix = new Matrix();
            matrix.translate((256-textField.width*2)/2*stormScaleRatioX, 0);
            matrix.scale(2.0, 2.0);
            maskMap.draw(textField, matrix);
            txtNum++;
            if (txtNum >= txt.length) {
                txtNum = 0;
            }
            
            reset();
        }
        
        /**
        *
        * Called when you need reset the view. E.G. resizing screen.
        */
        protected function reset():void
        {
            if(canvas)
                canvas.dispose();
            
            //build view, to optimize we use 25% size BitmapData of actual screensize.
            canvas = new BitmapData(stage.stageWidth, stage.stageHeight, false, 0x000000);
            canvasBitmap.bitmapData = canvas;
            
            //precalculate ratio that converts screen coordinate to stomMapBitmap coodinate;
            stormScaleRatioX = 1 / stage.stageWidth*255;
            stormScaleRatioY = 1 / stage.stageHeight*255;
            
           //canvasBitmap.bitmapData = stormMap0;  //if you want to see stormMap;
           //canvasBitmap.bitmapData = repulsionHistoryMap;
           //canvasBitmap.bitmapData = forceMap; //if you want to see forceMap;
           //canvasBitmap.bitmapData = maskMap; //if you want to see maskMap;
        }
        
        
        //We rebuild screen when swf is reseized.
        //It is not required if you only run it on wonderfl.
        protected function resizeHandler(e:Event):void
        {
            reset();
        }

        
        //Update storm and particle every frame.
        protected function enterFrameHandler(e:Event):void
        { 
           updateStormMap();
           updateRepulsionMap();
           updateForceMap();
           //updateMaskMap();
           updateParticles();
        }
        
        /**
        * We calculate current forcefielde's state by blending two stormMap.
        * Therefore force field gradually changes every frame.
        */
        protected function updateStormMap():void
        {
            stormCycle++;
            if(stormCycle == 180){
                stormMap0.perlinNoise(128,128,3,Math.random()*100,false,true,3,false);
            }else if(stormCycle==360){
                stormMap0.perlinNoise(128,128,3,Math.random()*100,false,true,3,false);
                stormCycle = 0;
            }
        }
        
        /**
        * We update repulsionMap according to the current mouse position.
        */
        protected function updateRepulsionMap():void
        {
            tempMat.a = 1;
            tempMat.b = 0;
            tempMat.c = 0;
            tempMat.d = 1;
            tempMat.tx = 0;
            tempMat.ty = 0;
            
            repulsionHistoryMap.draw(repulsionFadeMap, tempMat);
            
            tempMat.translate(-128,-128);
            
            tempMat.scale(1/stormScaleRatioX, 1/stormScaleRatioY);
            tempMat.translate(mouseX*stormScaleRatioX, mouseY*stormScaleRatioY);
            repulsionHistoryMap.draw(repulsionMap, tempMat, null, BlendMode.HARDLIGHT);
        }
        
        protected function updateForceMap():void
        {
            //generate current force fieldes state with blending two stormMaps.
            forceMap.copyPixels(stormMap0, stormMap0.rect, tempPt);
            tempColt.alphaMultiplier = Math.cos(stormCycle*Math.PI/180)*0.5+0.5;
            forceMap.draw(stormMap0, null, tempColt);
            
            //add mouse repulsion force
            forceMap.draw(repulsionHistoryMap,null,null,BlendMode.HARDLIGHT);
        }
        
        protected function updateMaskMap():void {
            //add mouse repulsion force
            forceMap.draw(maskMap,null,null,BlendMode.HARDLIGHT);
        }
        
        protected function updateParticles():void
        {
            var forceBytes:ByteArray = forceMap.getPixels(forceMap.rect);
            var maskBytes:ByteArray = maskMap.getPixels(maskMap.rect);
            
            var stageW:int = canvas.width;
            var stageH:int = canvas.height;
            var loopW:int = stageW-1;
            var loopH:int = stageH-1;
            var byteIndex:int;
            
            canvas.lock();
            canvas.colorTransform(canvas.rect, fadeCanvasColt);
            
            //Update Paritcle position and draw.
            var col:int, r:int, g:int, b:int;
            for(var i:int=0; i<particleNum; i++)
            {
                var prt:Particle = particles[i];
                byteIndex = (int(prt.y*stormScaleRatioY)*256 + int(prt.x*stormScaleRatioX))<<2;
                prt.vx = (prt.vx * 0.99 + (forceBytes[byteIndex+1]-128)*prt.scaleFactor);
                prt.vy = (prt.vy * 0.99 + (forceBytes[byteIndex+2]-128)*prt.scaleFactor);
                prt.x -= prt.vx;
                prt.y -= prt.vy;
                
                if (maskBytes[byteIndex + 1] == 0) {
                    prt.vx *= -2;
                    prt.vy *= -2;
                }
                
                if(prt.x<0){
                    prt.x = loopW;
                }else if(prt.x > loopW){
                    prt.x = 1;
                }
                if(prt.y<0){
                    prt.y = loopH;
                }else if(prt.y > loopH){
                    prt.y = 1;
                }

                //trace(maskBytes[byteIndex + 1]);
                
                //Self implimentation of addtive color blend mode.
                //Because there is too many particle, blending particle with low brightness color is much effective.
                col = canvas.getPixel(int(prt.x), int(prt.y));
                r = (col>>16&0xff) + prt.r;
                g = (col>>8&0xff) + prt.g;
                b = (col&0xff) + prt.b;
                r = (r<0xff)? r : 0xff;
                g = (g<0xff)? g : 0xff;
                b = (b<0xff)? b : 0xff;
                canvas.setPixel(int(prt.x), int(prt.y), (r<<16)|(g<<8)|b);
            }
            //canvas.applyFilter(canvas, canvas.rect, tempPt, canvasBlurFilter);
            canvas.unlock();
        }
    }
    
}


class Particle
{
    //particle position
    public var x:Number = 0;
    public var y:Number = 0;
    public var vx:Number = 0;
    public var vy:Number = 0;
    
    //particle color
    public var r:int;
    public var g:int;
    public var b:int;
       
    //scale factor that affects the force applied to the particle.
    public var scaleFactor:Number;
}