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

MOTION-FLUID-SOUND (forked from: Instrument)

Motion controlled fluid dynamics with a bit o' sound. Press space bar to start it. A webcam is required.
/**
 * Copyright devon_o ( http://wonderfl.net/user/devon_o )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/mdHR
 */

// forked from Instrument
package  {
    
    import com.bit101.components.Label;
    import com.bit101.components.Style;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.BlendMode;
    import flash.display.Shape;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.events.StatusEvent;
    import flash.filters.BlurFilter;
    import flash.geom.ColorTransform;
    import flash.geom.Matrix;
    import flash.geom.Point;
    import flash.media.Camera;
    import flash.media.Video;
    import flash.utils.setTimeout;
    import net.hires.debug.Stats;
    /**
     *     __  __  ___ _____ ___ ___  _   _           _____ _     _   _ ___ ____          ____   ___  _   _ _   _ ____  
     *    |  \/  |/ _ \_   _|_ _/ _ \| \ | |         |  ___| |   | | | |_ _|  _ \        / ___| / _ \| | | | \ | |  _ \ 
     *    | |\/| | | | || |  | | | | |  \| |  _____  | |_  | |   | | | || || | | |_____  \___ \| | | | | | |  \| | | | |
     *    | |  | | |_| || |  | | |_| | |\  | |_____| |  _| | |___| |_| || || |_| |_____|  ___) | |_| | |_| | |\  | |_| |
     *    |_|  |_|\___/ |_| |___\___/|_| \_|         |_|   |_____|\___/|___|____/        |____/ \___/ \___/|_| \_|____/ 
     *                                                                                                          
     * 
     * Fluid dynamics based on this processing script: http://www.processing.org/learning/topics/fluid.html
     * There are other Wonderfl projects based on the same Processing code ( such as this one: http://wonderfl.net/c/7ZnV ) and, though they may
     * actually perform better, I wanted to go back to the source and do it my own way.
     * 
     * 
     * @author Devon O.
     * 22/10/2010 10:52
     */
    
    [SWF(width='465', height='465', backgroundColor='#000000', frameRate='31')]
    public class Main extends Sprite {
        
        public static const NUM_PARTICLES:int = 3000;
        public static const RESOLUTION:int = 15;
        public static const PEN_SIZE:int = 30;
        
        public static var cols:int;
        public static var rows:int;
        public static var w:Number;
        public static var h:Number;
        public static var v:Vector.<Vector.<VSquare>>;
        public static var vbuf:Vector.<Vector.<VBuffer>>;
        
        private var _particles:Vector.<Particle> = new Vector.<Particle>(NUM_PARTICLES, true);
        private var _pcount:int = 0;
        private var _mouseXvel:int = 0;
        private var _mouseYvel:int = 0;
        private var _prevGuideX:Number = 0.0;
        private var _prevGuideY:Number = 0.0;
        
        private var _pHolder:Shape = new Shape();
        private var _display:BitmapData;
        private var _blur:BlurFilter = new BlurFilter(4, 4, 2);
        private var _pt:Point = new Point();
        private var _redden:ColorTransform = new ColorTransform(1, .50, .50, 1);
        
        private var _video:Video;
        private var _tracker:Tracker;
        private var _motionGuide:Shape;
        private var _motionHolder:Sprite;
        private var _previewMatrix:Matrix = new Matrix();
        private var _previewTransform:ColorTransform = new ColorTransform(.3, .3, .3, .5);
        
        private var _toneholder:Sprite;
        private var _tones:Vector.<ToneCircle> = new Vector.<ToneCircle>(16, true);
        
        private var _draw:Boolean = false;
        private var _showCircles:Boolean = false;
        private var _showVideo:Boolean = false;
        private var _inited:Boolean = false;
        private var _t:Label;
        
        public function Main() {
            if (stage) showText();
            else addEventListener(Event.ADDED_TO_STAGE, showText);
        }
        
        private function showText(event:Event = null):void {
            Style.LABEL_TEXT = 0xFFFFFF;
            _t = new Label(this, 0, 0, "Move around in front of your webcam to stir up the liquid and create music.\n\nPress A to show/hide webcam image.\nPress S to show/hide guides.\n\nPress SPACE to begin or stop.");
            _t.x = (stage.stageWidth - _t.width) >> 1;
            _t.y = 175;
            stage.addEventListener(KeyboardEvent.KEY_DOWN, onKey);
        }
        
        private function init():void {
            w = stage.stageWidth;
            h = stage.stageHeight;
            cols = w / RESOLUTION;
            rows = h / RESOLUTION;
            _prevGuideX = stage.stageWidth >> 1;
            _prevGuideY = stage.stageHeight >> 1;
            
            if (initVideo()) {
                initDisplay();
                initFluid();
                initToneGrid();
                //addChild(new Stats());
                setTimeout(begin, 1000);
                addEventListener(Event.ENTER_FRAME, frameHandler);
            }
        }
        
        private function begin():void {
            _draw = true;
        }
        
        private function initDisplay():void {
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;
            stage.showDefaultContextMenu = false;
            _display = new BitmapData(w, h, false, 0x000000);
            addChild(new Bitmap(_display));
        }
        
        private function onKey(event:KeyboardEvent):void {
            // 65=a 83=s 32=space
            if (event.keyCode == 65) _showVideo = !_showVideo;
            if (event.keyCode == 83) _showCircles = !_showCircles;
            if (event.keyCode == 32) {
                if (!_inited) {
                    _inited = true;
                    removeChild(_t);
                    init();
                } else {
                    if (!willTrigger(Event.ENTER_FRAME)) {
                        addEventListener(Event.ENTER_FRAME, frameHandler);
                    } else {
                        removeEventListener(Event.ENTER_FRAME, frameHandler);
                    }
                }
            }
        }
        
        private function initVideo():Boolean {
            _previewMatrix.scale(.25, .25);
            
            _motionGuide = new Shape();
            _motionGuide.graphics.lineStyle(0, 0xFFFFFF, .25);
            _motionGuide.graphics.drawCircle(0, 0, 10);
            _motionHolder = new Sprite();
            _motionHolder.addChild(_motionGuide);
            
            _video = new Video(stage.stageWidth, stage.stageHeight);
            var camIndex:int = 0;
            for ( var i:int = 0 ; i < Camera.names.length ; i++ ) {
                if ( Camera.names[ i ] == "USB Video Class Video" ) {
                    camIndex = i;
                    break;
                }
            }
            var cam:Camera = Camera.getCamera(String(camIndex));
            
            if (cam != null) {
                cam.setMode(stage.stageWidth, stage.stageHeight, 25);
                _video.attachCamera(cam);
                _tracker = new Tracker(_video);
                return true;
            } else {
                _t.text = "No webcam detected.";
                addChild(_t);
                return false;
            }
        }
        
        private function initFluid():void {
            v = new Vector.<Vector.<VSquare>>();
            vbuf = new Vector.<Vector.<VBuffer>>();
            
            for (var i:int = 0; i < NUM_PARTICLES; i++) {
                _particles[i] = new Particle(Math.random() * w, Math.random() * h);
            }
            
            for (var col:int = 0; col <= cols; col++) {
                
                v[col] = new Vector.<VSquare>();
                vbuf[col] = new Vector.<VBuffer>();
                
                for (var row:int = 0; row <= rows; row++) {
                    
                    v[col][row] = new VSquare(col * RESOLUTION, row * RESOLUTION);
                    vbuf[col][row] = new VBuffer(col * RESOLUTION, row * RESOLUTION);
                    
                }
            }
            v.fixed = true;
            vbuf.fixed = true;
        }
        
        private function initToneGrid():void {
            _toneholder = new Sprite();
            var ctr:int = 0;
            var hStep:int = int(w / 4);
            var vStep:int = int(h / 4);
            var xpos:int = hStep  * .5;
            var ypos:int = vStep * .5;
            for (var i:int = 0; i < 4; i++) {
                for (var j:int = 0; j < 4; j++) {
                    var tc:ToneCircle = new ToneCircle(4000 - (ctr * 200 + 500));
                    tc.x = xpos;
                    tc.y = ypos;
                    xpos += hStep;
                    _toneholder.addChild(tc);
                    _tones[ctr] = tc;
                    ctr++;
                }
                xpos = hStep * .5;
                ypos += vStep;
            }
        }
        
        private function frameHandler(event:Event):void {
            updateTracker();
            updateParticles();
            checkForTone();
            
            if (_draw) {
                if (_showVideo) {
                    _display.draw(_tracker._previous, _previewMatrix, _previewTransform);
                }
                if (_showCircles) {
                    _display.draw(_toneholder);
                    _display.draw(_motionHolder);
                }
                _display.applyFilter(_display, _display.rect, _pt, _blur);
                _display.draw(_pHolder, null, _redden, BlendMode.ADD);
            }
        }
        
        private function checkForTone():void {
            var i:int = _tones.length;
            while (i--) {
                var tc:ToneCircle = _tones[i];
                var dx:Number = _motionGuide.x - tc.x;
                var dy:Number = _motionGuide.y - tc.y;
                var d:Number = dx * dx + dy * dy;
                
                // 55 is radius of motionguide + radius of tc
                if (d < 55 * 55) {
                    if (!tc.played) tc.playTone();
                } else if (tc.played) {
                    tc.played = false;
                }
            }
        }
        
        private function updateTracker():void {
            _tracker.track();
            _motionGuide.x += (_tracker.x - _motionGuide.x) / 10;
            _motionGuide.y += (_tracker.y - _motionGuide.y) / 10;
        }
        
        private function updateParticles():void {
            var axvel:int = _motionGuide.x - _prevGuideX;
            var ayvel:int = _motionGuide.y - _prevGuideY;
            
            _mouseXvel = (axvel != _mouseXvel) ? axvel : 0;
            _mouseYvel = (ayvel != _mouseYvel) ? ayvel : 0;
            
            for (var col:int = 1; col < cols; col++) {
                for (var row:int = 1; row < rows; row++) {
                    vbuf[col][row].update(col, row);
                    v[col][row].addBuffer(col, row);
                    
                    var adj:Number = v[col][row].x - _motionGuide.x;
                    var opp:Number = v[col][row].y - _motionGuide.y;
                    var d:Number = Math.sqrt(opp * opp + adj * adj);
                    
                    if (d < PEN_SIZE) {
                        if (d < 4) d = PEN_SIZE;
                        var mod:Number = PEN_SIZE / d;
                        v[col][row].xvel += _mouseXvel * mod;
                        v[col][row].yvel += _mouseYvel * mod;
                    }
                    
                    v[col][row].xvel *= 0.98;
                    v[col][row].yvel *= 0.98;
                }
            }
            
            _pHolder.graphics.clear();
            
            var i:int = NUM_PARTICLES;
            while(i--) {
                var p:Particle = _particles[i];
                
                p.update();
                
                var dx:Number = p.tx - p.x;
                var dy:Number = p.ty - p.y;
                d = (dx * dx + dy * dy);
                var lim:Number = Math.random() ;
                if (d < lim) {
                    p.tx = p.x + lim;
                    p.ty = p.y + lim;
                }
                p.color = d * 50 << 16 | d * 10 << 8 | 0xFF;
                
                
                _pHolder.graphics.lineStyle(0, p.color);
                _pHolder.graphics.moveTo(p.x, p.y);
                _pHolder.graphics.lineTo(p.tx, p.ty);
                
                p.tx = p.x;
                p.ty = p.y;
            }
            
            _prevGuideX = _motionGuide.x;
            _prevGuideY = _motionGuide.y;
        }
    }
}


/*
  ____            _   _      _      
 |  _ \ __ _ _ __| |_(_) ___| | ___ 
 | |_) / _` | '__| __| |/ __| |/ _ \
 |  __/ (_| | |  | |_| | (__| |  __/
 |_|   \__,_|_|   \__|_|\___|_|\___|
                                   
*/
class Particle {
    
    public var x:Number;
    public var y:Number
    public var xvel:Number;
    public var yvel:Number;
    public var pos:int;
    public var tx:Number;
    public var ty:Number;
    public var color:uint = 0xFFFFFF;

    public function Particle(xIn:Number, yIn:Number) {
        x = tx = xIn;
        y = ty = yIn;
    }
    
    public function update():void {
        
        if (x > 0 && x < Main.w && y > 0 && y < Main.h) {
            
            var vi:int = x / Main.RESOLUTION;
            var vu:int = y / Main.RESOLUTION;
            
            var ax:Number = (x % Main.RESOLUTION) / Main.RESOLUTION;
            var ay:Number = (y % Main.RESOLUTION) / Main.RESOLUTION;
            
            xvel += (1 - ax) * Main.v[vi][vu].xvel * 0.05;
            yvel += (1 - ay) * Main.v[vi][vu].yvel * 0.05;
            
            try {
            xvel += ax * Main.v[vi+1][vu].xvel * 0.05;
            yvel += ax * Main.v[vi+1][vu].yvel * 0.05;
      
            xvel += ay * Main.v[vi][vu + 1].xvel * 0.05;
            yvel += ay * Main.v[vi][vu + 1].yvel * 0.05;
            } catch (e:Error) { };
            
            x += xvel;
            y += yvel;
            
            
        } else {
            x = tx = Math.random() * Main.w;
            y = ty = Math.random() * Main.h;
            xvel = 0;
            yvel = 0;
        }
        
        xvel *= 0.5;
        yvel *= 0.5;
    }
}

/*
 __     ______         __  __           
 \ \   / / __ ) _   _ / _|/ _| ___ _ __ 
  \ \ / /|  _ \| | | | |_| |_ / _ \ '__|
   \ V / | |_) | |_| |  _|  _|  __/ |   
    \_/  |____/ \__,_|_| |_|  \___|_|   
*/
class VBuffer {
    
    public var x:int;
    public var y:int;
    public var xvel:Number;
    public var yvel:Number;
    public var pressurex:Number = 0.0;
    public var pressurey:Number = 0.0;
    public var pressure:Number = 0.0;
    
    public function VBuffer(xIn:int, yIn:int) {
        x = xIn;
        y = yIn;
    }
    
    public function update(i:int, u:int):void {
        pressurex = (Main.v[i - 1][u - 1].xvel * 0.5 + Main.v[i - 1][u].xvel + Main.v[i - 1][u + 1].xvel * 0.5 - Main.v[i + 1][u - 1].xvel * 0.5 - Main.v[i + 1][u].xvel - Main.v[i + 1][u + 1].xvel * 0.5);
        pressurey = (Main.v[i - 1][u - 1].yvel * 0.5 + Main.v[i][u - 1].yvel + Main.v[i + 1][u - 1].yvel * 0.5 - Main.v[i - 1][u + 1].yvel * 0.5 - Main.v[i][u + 1].yvel - Main.v[i + 1][u + 1].yvel * 0.5);
        pressure = (pressurex + pressurey) * 0.25;
    }
}

/*
 __     ______                             
 \ \   / / ___|  __ _ _   _  __ _ _ __ ___ 
  \ \ / /\___ \ / _` | | | |/ _` | '__/ _ \
   \ V /  ___) | (_| | |_| | (_| | | |  __/
    \_/  |____/ \__, |\__,_|\__,_|_|  \___|
                   |_|                     
*/
class VSquare {
    
    public var x:int;
    public var y:int;
    public var xvel:Number = 0.0;
    public var yvel:Number = 0.0;
    public var col:Number;
    
    public function VSquare(xIn:int, yIn:int) {
        x = xIn;
        y = yIn;
    }
    
    public function addBuffer(i:int, u:int):void {
        if (i > 0 && i < Main.cols && u > 0 && u < Main.rows) {
            xvel     += (Main.vbuf[i-1][u-1].pressure * 0.5
                    + Main.vbuf[i-1][u].pressure
                    + Main.vbuf[i-1][u+1].pressure * 0.5
                    - Main.vbuf[i+1][u-1].pressure * 0.5
                    - Main.vbuf[i+1][u].pressure
                    - Main.vbuf[i+1][u+1].pressure * 0.5
                    ) * 0.25;
                    
            yvel     += (Main.vbuf[i-1][u-1].pressure * 0.5
                    + Main.vbuf[i][u-1].pressure
                    + Main.vbuf[i+1][u-1].pressure * 0.5
                    - Main.vbuf[i-1][u+1].pressure * 0.5
                    - Main.vbuf[i][u+1].pressure
                    - Main.vbuf[i+1][u+1].pressure * 0.5
                    ) * 0.25;
        }
    }
}


/*
  _____                _              
 |_   _|_ __ __ _  ___| | __ ___ _ __ 
   | | | '__/ _` |/ __| |/ // _ \ '__|
   | | | | | (_| | (__|   <|  __/ |   
   |_| |_|  \__,_|\___|_|\_\\___|_| 
   
   Basic motion tracker - very roughly based on code by Soulwire ( http://blog.soulwire.co.uk/ )
*/
import flash.display.BlendMode;
import flash.filters.BlurFilter;
import flash.display.BitmapData;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.media.Video;

class Tracker {
    
    public var _previous:BitmapData;
    public var _current:BitmapData;
    public var x:Number;
    public var y:Number;
    
    private var _blur:BlurFilter = new BlurFilter(32, 32);
    private var _vid:Video;
    private var _mirror:Matrix;
    private var _point:Point = new Point();
    private var _area:Rectangle;
    private var _isMotion:Boolean = false;
    
    public function Tracker(vid:Video) {
        _vid = vid;
        _mirror = new Matrix();
        _mirror.scale( -1, 1);
        _mirror.translate(_vid.width, 0);
        
        _current = new BitmapData(_vid.width, _vid.height, false, 0x000000);
        _previous = _current.clone();
    }
    
    public function track():void {
        _current.draw(_vid, _mirror);
        _current.draw(_previous, null, null, BlendMode.DIFFERENCE);
        _current.applyFilter(_current, _current.rect, _point, _blur);
        _current.threshold(_current, _current.rect, _point, ">",  0xFF333333, 0xFFFFFFFF);
        _previous.draw(_vid, _mirror);
        
        _area = _current.getColorBoundsRect(0xFFFFFFFF, 0xFFFFFFFF, true);
        _isMotion = ( _area.width > ( _vid.width / 100) * 10 || _area.height > (_vid.height / 100) * 10 );
            
        if ( _isMotion ) {
            x = _area.x + (_area.width * .5);
            y = _area.y + (_area.width * .5);
        }
    }
}

/*
  _____                  ____ _          _      
 |_   _|___  _ __   ___ / ___(_)_ __ ___| | ___ 
   | | / _ \| '_ \ / _ \ |   | | '__/ __| |/ _ \
   | || (_) | | | |  __/ |___| | | | (__| |  __/
   |_| \___/|_| |_|\___|\____|_|_|  \___|_|\___|
   
*/
import flash.display.Shape;

class ToneCircle extends Shape {
    
    private var _tone:Tone;
    public var played:Boolean = false;
    
    public function ToneCircle(freq:Number) {
        _tone = new Tone(freq);
        graphics.lineStyle(0, 0xFFFFFF, .25);
        graphics.drawCircle(0, 0, 50);
    }
    
    public function playTone():void {
        _tone.play();
        played = true;
    }
}

/*
  _____                 
 |_   _|___  _ __   ___ 
   | | / _ \| '_ \ / _ \
   | || (_) | | | |  __/
   |_| \___/|_| |_|\___|
   
from Keith Peters (aka Bit-101 ) ( http://www.bit-101.com/blog/?p=2681 )
*/
import flash.media.Sound;
import flash.events.SampleDataEvent;
import flash.events.Event;

class Tone {
    
    protected const RATE:Number = 44100;
    protected const PI2:Number = Math.PI * 2;
    
    protected var _position:int = 0;
    protected var _sound:Sound;
    protected var _numSamples:int = 2048;
    protected var _samples:Vector.<Number>;
    protected var _isPlaying:Boolean = false;

    protected var _frequency:Number;

    public function Tone(frequency:Number) {
        _frequency = frequency;
        _sound = new Sound();
        _sound.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
        _samples = new Vector.<Number>();
        createSamples();
    }

    protected function createSamples():void {
        var amp:Number = 1.0;
        var i:int = 0;
        var mult:Number = frequency / RATE * PI2;
        while(amp > 0.001) {
            _samples[i] = Math.sin(i * mult) * amp;
            amp *= 0.99993;
            i++;
        }
        _samples.length = i;
    }

    public function play():void {
        if(!_isPlaying){
            _position = 0;
            _sound.play();
            _isPlaying = true;
        }
    }

    protected function onSampleData(event:SampleDataEvent):void {
        for (var i:int = 0; i < _numSamples; i++){
            if(_position >= _samples.length){
                _isPlaying = false;
                return;
            }
            event.data.writeFloat(_samples[_position]);
            event.data.writeFloat(_samples[_position]);
            _position++;
        }
    }

    public function set frequency(value:Number):void{
        _frequency = value;
        createSamples();
    }
    public function get frequency():Number{
        return _frequency;
    }
}