Creating Cool Animation Effect Using HTML5 Canvas

HTML5 is new technology that is developed as a replacement for current HTML standard. HTML5 brings so many improvement that not exists in current standard HTML. Canvas is one of the most important element of HTML5. This new element allows us to draw directly and manipulate any pixels on canvas. The canvas has a context that provides us cool high level API to help us when drawing onto canvas.

In this tutorial we’ll try to create a simple animation effect using HTML5 canvas. Although the animation we’ll create is just a simple animation, but it’s pretty cool to see. Here is a screenshot of the final animation. If you want to see it lives, you can see the final animation by clicking Live Preview button above.

Cool Animation Effect
Cool Animation Effect

Basic Idea

When you see the animation, may be you think it’s difficult to create such animation. As I stated before, this is simple and easy to create the animation like that. The idea is creating invisible particles with random location on the canvas then move every particles randomly. As the particles moved, we check the distance between particles, if the distances is less than, say 230 pixels, we create random color line. Then the logic is repeated.

Define Canvas Object

We’ll work on HTML5 canvas, so we must define it first.

<div style="width:800px;padding-top:20px; margin:0 auto;">
    <canvas id="canvas" width="800" height="500"></canvas>
</div>

The context of the canvas can be gotten by invoking [cci]getContext(‘2d’)[/cci] on canvas object. This will give us the 2d context.

// get canvas element
var canvas = document.getElementById('canvas');
if(!canvas.getContext) {
    alert("Your browser doesn't support html5");
    return;
}

// get drawing context
ctx = canvas.getContext("2d");

Define Particle Object

For simplicity and easy to manage the particles, we create a javascript object which represent a particle. Our particle has the following properties:

  1. Position
    Position property define the particle’s location on the canvas. It contains [cci]x[/cci] and [cci]y[/cci] variables which represent the particle’s coordinat on the canvas. The position property will be generated randomly.
  2. Radius
    Radius property defines the particle radius. Because we want to create an invisible particle, we’ll put [cci]0[/cci] as radius value.
  3. Speed
    Speed property determines how fast a particle can move
  4. Color
    Color property determines particle’s color. Because we want to create an invisible particle, this property will be used to color the line that connecting this particle with others. This property will be generated randomly
  5. Steering Angle
    Steering angle determines the particle’s movement direction. Steering angle has range from 0 to 360 and will be generated randomly.

Below is the particle object definition on Javascript:

// Particle object
var Particle = function() {
    // particle position
    this.x = Math.random()*width;
    this.y = Math.random()*height;
    
    // particle radius, 0 means invisible particle
    this.rad = 0;
    
    // particle speed
    this.speed = 4;
    
    // generate random particle color
    var r = Math.round(Math.random()*255);
    var g = Math.round(Math.random()*255);
    var b = Math.round(Math.random()*255);
    var a = Math.random();
    this.color = "rgba("+r+","+g+","+b+","+a+")";

    //particle steering angle, 0 to 360
    this.angle = Math.round(Math.random()*Math.PI*2);
};         

Variables [cci]width[/cci] and [cci]height[/cci] are respectively represent the canvas width and height. It is defined like this:

width = canvas.width;
height = canvas.height;

After the particle object definition is complete, we can populate a random particles by using this way:

// populate particles
for(var i=0; i< NUM_PARTICLES; i++) {
    particles.push(new Particle());
}

Variable [cci]NUM_PARTICLES[/cci] is global variable that defines the number of particle that will be generated.

Render The Particle

Now, we’ll add the rendering method for a particle. This method responsibles to render the particle that respect to the particle’ properties. This method also responsible to check the distance of the particle from another particles. If the distance is less than 230 pixels, the line will drawn connecting this particle to other.

// particle rendering method
this.render = function() {
    // render the particle
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.rad, 0, 2*Math.PI, false);
    ctx.fillStyle = this.color;
    ctx.fill();
    
    // count distance from another particle
    // if the distance less than 230px, connect the particles with line                    
    for(var i=0; i<NUM_PARTICLES; i++) {
        var p2 = particles[i];                        
        var dx =  p2.x - this.x;
        var dy =  p2.y - this.y;
        // count the distance
        var distance = Math.sqrt(dx*dx + dy*dy);
        
        // if distance less than 230px, draw a line to connecting between two particles
        if(distance < 230) {
            ctx.beginPath();
            ctx.lineWidth = 1;
            ctx.moveTo(this.x, this.y);
            ctx.lineTo(p2.x, p2.y);
            ctx.strokeStyle = this.color;
            ctx.stroke();
        }
    }
};

Move The Particle

Our animation will be nonsense if the particles don’t move. We’ll give the particle ability to move by adding a function that handle the movement into particle object. This function uses a simple vector operation to move the particles. The [cci]x[/cci] coordinat property will be increased by the speed multiplied by the cosinus of steering angle. The [cci]y[/cci] coordinat property will be increased by the speed multiplied by the sinus of steering angle. Because the particles is always move, there is a time when the particle reach the canvas border. To avoid the particles moves out of canvas, we’ll move the particles to opposite side when the border is reached.

// particle moving method
this.move = function(){
    this.x += this.speed*Math.cos(this.angle);
    this.y += this.speed*Math.sin(this.angle);
    
    //avoid particle to move out of canvas
    if(this.x < 0) this.x = width;
    if(this.y < 0) this.y = height;
    if(this.x > width) this.x = 0;
    if(this.y > height) this.y = 0;
};

Render The Canvas

We have our particle ready, now it’s time to render the canvas. Rendering the canvas means rendering all of the particles including the canvas background.

var render = function() {
    // render the background
    ctx.fillStyle = "rgba(0,0,0,0.1)";
    ctx.fillRect(0, 0, width, height);

    // render particles
    for(var i=0; i<NUM_PARTICLES; i++){
        var p = particles[i];
        p.render();
        p.move();
    }
};

Render Scheduling

To make the animation life, we must schedule to call [cci]render[/cci] method. We want to set FPS (Frame Per Second) to be 30, so we must set the interval to be [cci]1000/FPS ms[/cci]

// setup FPS = Frame Per Second
// render function will be called every 1000/FPS = 33 ms
setInterval(render, 1000/FPS);

Final Source Code

This is the final source code. Save it as html file and open it in your favorite browser.

<html>
    <head>
        <title>Cool Animation Effect Using HTML5 Canvas</title>
        <script type="text/javascript">
            // define FPS(Frame Per Second)
            var FPS = 30;
            // define number of particles in the system
            var NUM_PARTICLES = 20;
            
            // hold the canvas width and height
            var width, height;
            
            // hold the canvas context
            var ctx;
            
            // array of particles
            var particles = [];
            
            // Particle object
            var Particle = function() {
                // particle position
                this.x = Math.random()*width;
                this.y = Math.random()*height;
                
                // particle radius, 0 means invisible particle
                this.rad = 0;
                
                // particle speed
                this.speed = 4;
                
                // generate random particle color
                var r = Math.round(Math.random()*255);
                var g = Math.round(Math.random()*255);
                var b = Math.round(Math.random()*255);
                var a = Math.random();
                this.color = "rgba("+r+","+g+","+b+","+a+")";
        
                //particle steering angle, 0 to 360
                this.angle = Math.round(Math.random()*Math.PI*2);
                
                // particle rendering method
                this.render = function() {
                    // render the particle
                    ctx.beginPath();
                    ctx.arc(this.x, this.y, this.rad, 0, 2*Math.PI, false);
                    ctx.fillStyle = this.color;
                    ctx.fill();
                    
                    // count distance from another particle
                    // if the distance less than 230px, connect the particles with line                    
                    for(var i=0; i<NUM_PARTICLES; i++) {
                        var p2 = particles[i];                        
                        var dx =  p2.x - this.x;
                        var dy =  p2.y - this.y;
                        // count the distance
                        var distance = Math.sqrt(dx*dx + dy*dy);
                        
                        // if distance less than 230px, draw a line to connecting between two particles
                        if(distance < 230) {
                            ctx.beginPath();
                            ctx.lineWidth = 1;
                            ctx.moveTo(this.x, this.y);
                            ctx.lineTo(p2.x, p2.y);
                            ctx.strokeStyle = this.color;
                            ctx.stroke();
                        }
                    }
                };
                
                // particle moving method
                this.move = function(){
                    this.x += this.speed*Math.cos(this.angle);
                    this.y += this.speed*Math.sin(this.angle);
                    
                    //avoid particle to move out of canvas
                    if(this.x < 0) this.x = width;
                    if(this.y < 0) this.y = height;
                    if(this.x > width) this.x = 0;
                    if(this.y > height) this.y = 0;
                };
            };            
            var render = function() {
                // render the background
                ctx.fillStyle = "rgba(0,0,0,0.1)";
                ctx.fillRect(0, 0, width, height);

                // render particles
                for(var i=0; i<NUM_PARTICLES; i++){
                    var p = particles[i];
                    p.render();
                    p.move();
                }
            };
            window.onload = function() {
                // get canvas element
                var canvas = document.getElementById('canvas');
                if(!canvas.getContext) {
                    alert("Your browser doesn't support html5");
                    return;
                }

                width = canvas.width;
                height = canvas.height;

                // get drawing context
                ctx = canvas.getContext("2d");

                // populate particles
                for(var i=0; i< NUM_PARTICLES; i++) {
                    particles.push(new Particle());
                }

                // setup FPS = Frame Per Second
                // render function will be called every 1000/FPS = 33 ms
                setInterval(render, 1000/FPS);
            };
        </script>
    </head>
    <body>
        <div style="width:800px;padding-top:20px; margin:0 auto;">
             <canvas id="canvas" width="800" height="500"></canvas>
        </div>
    </body>
</html>

Drawing Mandelbrot Set on HTML5 Graphics Canvas

Mandelbrot set is a mathematical set of points whose boundary is a distinctive and easily recognizable two-dimensional fractal shape. The Mandelbrot set is defined over the complex number plane. The definition of the Mandelbrot set is simple but the structure of the set is really complex and beautiful.

Mandelbrot Set Overview

Mandelbrot set is defined mathematically as the set of values of c in the complex plane for which the orbit of 0 under iteration of the complex quadratic polynomial zn+1 = zn2 + c remains bounded. That is, a complex number c is part of the Mandelbrot set if, when starting with z0 = 0 and applying the iteration repeatedly, the absolute value of zn remains bounded however large n gets.

Below is the structure of the Mandelbrot set that is generated using our application.

Mandelbrot Set
Mandelbrot Set

HTML 5 Canvas Overview

HTML5 is the latest version of the standard HTML. It comes with new cool element, Canvas that allows you to draw directly and manipulate every single pixels in the canvas. A canvas is a rectangular area on an HTML page, and it is specified with the [cci][/cci] element. HTML5 currently is under development and not all browser support it yet.

We’ll not cover all the definition of cool function in the canvas. There are documentation of those over the web. We only give an attention to several functions that will be used in our tutorial.

Every canvas element has a graphics context that will be used to modify the pixel on the canvas. We’ll use [cci]2d[/cci] context. To get the context, use [cci]getContext[/cci] function of the canvas object.

var canvas = document.getElementById('thecanvas');
var ctx = canvas.getContext('2d');

The above code assumes that we have defined a canvas element which id [cci]thecanvas[/cci].

<canvas id="thecanvas" width="500" height="500"></canvas>

Our requirement is to get all the pixels data of the canvas and modify the color of the pixels. To get all pixels data, use [cci]getImageData[/cci] function of the context. The function has 4 arguments [cci]x[/cci],[cci]y[/cci],[cci]width[/cci] and [cci]height[/cci]. [cci]x[/cci] and [cci]y[/cci] arguments define the square top left position from which the pixels will be taken. [cci]width[/cci] and [cci]height[/cci] define the dimension of the square.

var imageData = ctx.getImageData(0, 0, 100, 100);
var pixels = imageData.data;

The variable [cci]pixels[/cci] above contains 100×100=10.000 pixels that is save in 1 dimension array. 1 pixels have 4 elements, red,green,blue and alpha component. So [cci]pixels[/cci] variable has 10.000×4 = 40.000 elements. 1 element pixel is an integer from 0 to 255.

We now can modify the pixels data. After the pixels have been modified, we must put the pixels back to canvas in order to our modification takes effect.

ctx.putImageData(imageData, 0, 0);

Drawing Mandelbrot Set on HTML 5 Canvas

Javascript doesn’t provides built in complex number data type. So, we have to define our complex number data type. For simplicity of the tutorial, we will represent any complex number in javascript object with two properties. The [cci]x[/cci] property represents the real part and [cci]y[/cci] property represents the imaginary part.

//define complex data type
var Complex = function(x, y) {
   this.x = x; // real part
   this.y = y; // imaginary part
};

//define new complex number 1+2i
var complex = new Complex(1,2);

We have now a simple complex number data type for our application. Before we start to code, remember from above explanation. The mandelbrot definition requires to use two operation on complex number. We’ll not define all number operations in complex number, but we’ll create only two operations, multiplication and addition that will be used in generating mandelbrot set.

//define two complex number
var a = 1, b = 2, c = 3, d = 4;
var complex1 = new Complex(a,b);
var complex1 = new Complex(c,d);

//add complex1 and complex1 will result
var addition = new Complex(a+c, b+d);

//multiply complex1 and complex 2 will result
var multiplication = new Complex(a*c - b*d, b*c + a*d);

 

The Mandelbrot Set Drawing Algorithm

We’ll use different approach from mandelbrot set definition to visualize the set. Rather than finding all complex numbers [cci]c[/cci] which is bounded, our goal is to colorize all of the pixels in the canvas based on number of iterations. This will result great visualization for mandelbrot set.

Step by Step Mandelbrot Set Drawing

  1. Define user interface. Our application will has simple user interface. It’ll contains color scheme selection box, a button and the canvas.
    Mandelbrot Generator Simple User Interface
    Mandelbrot Generator Simple User Interface

    Below is HTML tags that defines our simple user interface.

<html>
<head>
<title>Drawing Mandelbrot set on HTML5 graphics canvas</title>
</head>
<body onload="setupCanvas();">
    
<div style="margin:0 auto;width:505px;padding-top:10px;">

<div>Color Scheme:
        <select id="scheme">
<option value="1" selected>Color Scheme 1</option>
<option value="2">Color Scheme 2</option>
<option value="3">Color Scheme 3</option>
<option value="4">Color Scheme 4</option>
<option value="5">Color Scheme 5</option>
<option value="6">Color Scheme 6</option>
        </select>
        <input type="button" value="Render" id="render_btn" onclick="render()"/>
        Scroll your mouse on the canvas to zoomin/zoomout
        </div>

        <canvas style="border:1px solid;" id="thecanvas" width="500" height="500"></canvas>
    </div>

</body>
</html>

After the html page is loaded, immediately we call javascript function [cci]setupCanvas[/cci]. This function will checks whether the browser support html5 canvas then get canvas’s drawing context and save it as global variable [cci]ctx[/cci]. We’ll use the context to modify each pixels on the canvas.

  • Define [cci]setupCanvas[/cci] function.
         
    var setupCanvas = function() {
        canvas = document.getElementById('thecanvas');
        setupMouseListener();
    
        if (!canvas.getContext) {
            alert("I'm sorry, seems your browser not support HTML 5, try another browser.");
            return;
        }
           
        // get drawing context
        ctx = canvas.getContext('2d');
    };   
    
  • Define render function.
    In this function we get the selected color scheme in the selection box. Save the canvas size as global variables then define the scaling ratio from canvas coordinats to mandelbrot coordinats.

 

var render = function() {
    if (!canvas.getContext) return;
    
    //get color scheme
    var a = document.getElementById('scheme');
    colorScheme = parseInt((a.value || a.options[a.selectedIndex].value));
    
    //save canvas size into variable
    cw = canvas.width;
    ch = canvas.height;
    
    //define scaling ratio from canvas coordinat to mandlebort coordinat
    scaleX = (mx[1] - mx[0])/cw;
    scaleY = (my[1] - my[0])/ch;
    
    drawMandlebrot();
};
  • Define the [cci]drawMandelbrot[/cci] function.
    In this function, we iterate to each pixels in the canvas and find the iteration for each pixels then color the pixel based on number iterations.

    
    var drawMandlebrot = function() {
        if(isDrawing) return;
        
        isDrawing = true;
        
        var imageData = ctx.getImageData(0, 0, cw, ch);
        pixels = imageData.data;
        
        for(var x = 0; x < cw; x++) {
            for(var y = 0; y < ch; y++) {
                iteration = 0;
                // scale canvas coordinat to mandlebrot complex coordinat
                var x0 = scaleX*x + mx[0], y0 = scaleY*y + my[0];
                // define complex number c
                var c = new Complex(x0, y0), z = new Complex(0, 0);
                
                while(iteration < maxIteration) { // check if z still inbound if(z.x*z.x + z.y*z.y >= 2*2) {
                        break;
                    }
                    
                    // multiply z and z
                    z = new Complex(z.x*z.x - z.y*z.y, z.y*z.x + z.x*z.y);
                    // add z and c
                    z = new Complex(z.x+c.x, z.y+c.y);
                    // inrease iteration
                    iteration++;
                }
                
                // colorize current pixel
                setPixelColor(x, y, iteration);
            }
        }
        
        ctx.putImageData(imageData, 0, 0);
        isDrawing = false;
    };        
    
  • Define [cci]setPixelColor[/cci]
    This function will color the current pixels and get the color from [cci]getColor[/cci] function. [cci]getColor[/cci] function will generate a color based on number of iteration and selected color scheme.

    var getColor = function(i) {            
        i /= maxIteration;
        var cr = 0.0;
        var cg = 0.0;
        var cb = 0.0;
        
        switch(colorScheme) {
            case 1:
                if (i >= 0.66) cr = i;
                else if (i >= 0.33) cg = i;
                else cb = i;
                break;
            case 2:
                if (i >= 0.66) cr = i;
                else if (i >= 0.33) cb = i;
                else cg = i;
                break;
            case 3:
                if (i >= 0.66) cb = i;
                else if (i >= 0.33) cg = i;
                else cr = i;
                break;
            case 4:
                if (i >= 0.66) cg = i;
                else if (i >= 0.33) cr = i;
                else cb = i;
                break;
            case 5:
                if (i >= 0.66) cg = i;
                else if (i >= 0.33) cb = i;
                else cr = i;
                break;
            case 6:
                if (i >= 0.66) cb = i;
                else if (i >= 0.33) cr = i;
                else cg = i;
                break;
        };            
        
        var r = parseInt(cr * 3 * maxColor);
        var g = parseInt(cg * 3 * maxColor);
        var b = parseInt(cb * 3 * maxColor);
        return [r, g, b];
    };
    var setPixelColor = function(x, y, i) {
        var c = getColor(i);
        var off = 4 * (y * cw + x);
        pixels[off] = c[0]; //red
        pixels[off + 1] = c[1]; //green
        pixels[off + 2] = c[2]; //blue
        pixels[off + 3] = 255; //alpha
    };
    
  • Finally, add the zoom capability.
    var mouseWheelHandler = function(e) {
        // cross-browser wheel delta
        var e = window.event || e; // old IE support
        // get delta scroll
        var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
        
        //get mouse position
        var mousePos = getMousePos(e);
        
        // scale canvas coordinat to mandlebrot complex coordinat
        var x0 = scaleX*mousePos.x + mx[0], y0 = scaleY*mousePos.y + my[0];
        
        //scale the mandelbrot coordinat size
        var scale = 1 - delta*zoomScale;
        var sizeX = (mx[1]-mx[0])*scale;
        var sizeY = (my[1]-my[0])*scale;
        
        // redefine mandelbrot boundaries
        mx = [x0-sizeX/2, x0+sizeX/2];
        my = [y0-sizeY/2, y0+sizeY/2];
        render();
    };        
    var getMousePos = function(evt) {
        var rect = canvas.getBoundingClientRect();
        return {
            x: evt.clientX - rect.left,
            y: evt.clientY - rect.top
        };
    };
    

Final Source Code

<html>
<head>
    <title>Drawing Mandelbrot set on HTML5 graphics canvas</title>
    <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20type%3D%22text%2Fjavascript%22%3E%0A%20%20%20%20%20%20%20%20var%20canvas%2C%20ctx%3B%0A%20%20%20%20%20%20%20%20var%20iteration%2C%20maxIteration%20%3D%20100%2C%20maxColor%20%3D%20255%3B%0A%20%20%20%20%20%20%20%20var%20cw%2C%20ch%2C%20scaleX%2C%20scaleY%3B%0A%20%20%20%20%20%20%20%20var%20zoomScale%20%3D%200.3%3B%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%2F%2F%20color%20schemes%0A%20%20%20%20%20%20%20%20var%20colorScheme%20%3D%201%3B%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%2F%2Fdefine%20mandlebort%20boundaries%0A%20%20%20%20%20%20%20%20var%20mx%20%3D%20%5B-2%2C2%5D%2C%20my%20%3D%20%5B-2%2C2%5D%3B%0A%20%20%20%20%20%20%20%20%2F%2F%20hold%20all%20pixels%20image%20data%0A%20%20%20%20%20%20%20%20var%20pixels%3B%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20var%20isDrawing%20%3D%20false%3B%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%2F%2Fdefine%20complex%20data%20type%0A%20%20%20%20%20%20%20%20var%20Complex%20%3D%20function(x%2C%20y)%20%7B%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20this.x%20%3D%20x%3B%20%2F%2F%20real%20part%0A%20%20%20%20%20%20%20%20%20%20%20%20this.y%20%3D%20y%3B%20%2F%2F%20imaginary%20part%0A%20%20%20%20%20%20%20%20%7D%3B%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20var%20setupCanvas%20%3D%20function()%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20canvas%20%3D%20document.getElementById('thecanvas')%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20setupMouseListener()%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(!canvas.getContext)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alert(%22I'm%20sorry%2C%20seems%20your%20browser%20not%20support%20HTML%205%2C%20try%20another%20browser.%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20get%20drawing%20context%0A%20%20%20%20%20%20%20%20%20%20%20%20ctx%20%3D%20canvas.getContext('2d')%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20render()%3B%0A%20%20%20%20%20%20%20%20%7D%3B%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20var%20render%20%3D%20function()%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(!canvas.getContext)%20return%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2Fget%20color%20scheme%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20a%20%3D%20document.getElementById('scheme')%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20colorScheme%20%3D%20parseInt((a.value%20%7C%7C%20a.options%5Ba.selectedIndex%5D.value))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2Fsave%20canvas%20size%20into%20variable%0A%20%20%20%20%20%20%20%20%20%20%20%20cw%20%3D%20canvas.width%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20ch%20%3D%20canvas.height%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2Fdefine%20scaling%20ratio%20from%20canvas%20coordinat%20to%20mandlebort%20coordinat%0A%20%20%20%20%20%20%20%20%20%20%20%20scaleX%20%3D%20(mx%5B1%5D%20-%20mx%5B0%5D)%2Fcw%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20scaleY%20%3D%20(my%5B1%5D%20-%20my%5B0%5D)%2Fch%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20drawMandlebrot()%3B%0A%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20%20%20var%20drawMandlebrot%20%3D%20function()%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20if(isDrawing)%20return%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20isDrawing%20%3D%20true%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20imageData%20%3D%20ctx.getImageData(0%2C%200%2C%20cw%2C%20ch)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20pixels%20%3D%20imageData.data%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20for(var%20x%20%3D%200%3B%20x%20%3C%20cw%3B%20x%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for(var%20y%20%3D%200%3B%20y%20%3C%20ch%3B%20y%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20iteration%20%3D%200%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20scale%20canvas%20coordinat%20to%20mandlebrot%20complex%20coordinat%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20x0%20%3D%20scaleX*x%20%2B%20mx%5B0%5D%2C%20y0%20%3D%20scaleY*y%20%2B%20my%5B0%5D%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20define%20complex%20number%20c%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20c%20%3D%20new%20Complex(x0%2C%20y0)%2C%20z%20%3D%20new%20Complex(0%2C%200)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20while(iteration%20%3C%20maxIteration)%20%7B%20%2F%2F%20check%20if%20z%20still%20inbound%20if(z.x*z.x%20%2B%20z.y*z.y%20%3E%3D%202*2)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20multiply%20z%20and%20z%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20z%20%3D%20new%20Complex(z.x*z.x%20-%20z.y*z.y%2C%20z.y*z.x%20%2B%20z.x*z.y)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20add%20z%20and%20c%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20z%20%3D%20new%20Complex(z.x%2Bc.x%2C%20z.y%2Bc.y)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20inrease%20iteration%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20iteration%2B%2B%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20colorize%20current%20pixel%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20setPixelColor(x%2C%20y%2C%20iteration)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20ctx.putImageData(imageData%2C%200%2C%200)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20isDrawing%20%3D%20false%3B%0A%20%20%20%20%20%20%20%20%7D%3B%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20var%20getColor%20%3D%20function(i)%20%7B%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%2F%3D%20maxIteration%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20cr%20%3D%200.0%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20cg%20%3D%200.0%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20cb%20%3D%200.0%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20switch(colorScheme)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20case%201%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(i%20%3E%3D%200.66)%20cr%20%3D%20i%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20if%20(i%20%3E%3D%200.33)%20cg%20%3D%20i%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20cb%20%3D%20i%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20case%202%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(i%20%3E%3D%200.66)%20cr%20%3D%20i%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20if%20(i%20%3E%3D%200.33)%20cb%20%3D%20i%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20cg%20%3D%20i%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20case%203%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(i%20%3E%3D%200.66)%20cb%20%3D%20i%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20if%20(i%20%3E%3D%200.33)%20cg%20%3D%20i%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20cr%20%3D%20i%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20case%204%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(i%20%3E%3D%200.66)%20cg%20%3D%20i%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20if%20(i%20%3E%3D%200.33)%20cr%20%3D%20i%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20cb%20%3D%20i%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20case%205%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(i%20%3E%3D%200.66)%20cg%20%3D%20i%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20if%20(i%20%3E%3D%200.33)%20cb%20%3D%20i%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20cr%20%3D%20i%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20case%206%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(i%20%3E%3D%200.66)%20cb%20%3D%20i%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20if%20(i%20%3E%3D%200.33)%20cr%20%3D%20i%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20cg%20%3D%20i%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%3B%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20r%20%3D%20parseInt(cr%20*%203%20*%20maxColor)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20g%20%3D%20parseInt(cg%20*%203%20*%20maxColor)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20b%20%3D%20parseInt(cb%20*%203%20*%20maxColor)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20%5Br%2C%20g%2C%20b%5D%3B%0A%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20%20%20var%20setPixelColor%20%3D%20function(x%2C%20y%2C%20i)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20c%20%3D%20getColor(i)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20off%20%3D%204%20*%20(y%20*%20cw%20%2B%20x)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20pixels%5Boff%5D%20%3D%20c%5B0%5D%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20pixels%5Boff%20%2B%201%5D%20%3D%20c%5B1%5D%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20pixels%5Boff%20%2B%202%5D%20%3D%20c%5B2%5D%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20pixels%5Boff%20%2B%203%5D%20%3D%20255%3B%0A%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20%20%20var%20setupMouseListener%20%3D%20function()%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(canvas.addEventListener)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20IE9%2C%20Chrome%2C%20Safari%2C%20Opera%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20canvas.addEventListener(%22mousewheel%22%2C%20mouseWheelHandler%2C%20false)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Firefox%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20canvas.addEventListener(%22DOMMouseScroll%22%2C%20mouseWheelHandler%2C%20false)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20IE%206%2F7%2F8%0A%20%20%20%20%20%20%20%20%20%20%20%20else%20canvas.attachEvent(%22onmousewheel%22%2C%20mouseWheelHandler)%3B%0A%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20%20%20var%20mouseWheelHandler%20%3D%20function(e)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20cross-browser%20wheel%20delta%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20e%20%3D%20window.event%20%7C%7C%20e%3B%20%2F%2F%20old%20IE%20support%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20get%20delta%20scroll%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20delta%20%3D%20Math.max(-1%2C%20Math.min(1%2C%20(e.wheelDelta%20%7C%7C%20-e.detail)))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2Fget%20mouse%20position%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20mousePos%20%3D%20getMousePos(e)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20scale%20canvas%20coordinat%20to%20mandlebrot%20complex%20coordinat%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20x0%20%3D%20scaleX*mousePos.x%20%2B%20mx%5B0%5D%2C%20y0%20%3D%20scaleY*mousePos.y%20%2B%20my%5B0%5D%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2Fscale%20the%20mandelbrot%20coordinat%20size%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20scale%20%3D%201%20-%20delta*zoomScale%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20sizeX%20%3D%20(mx%5B1%5D-mx%5B0%5D)*scale%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20sizeY%20%3D%20(my%5B1%5D-my%5B0%5D)*scale%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20redefine%20mandelbrot%20boundaries%0A%20%20%20%20%20%20%20%20%20%20%20%20mx%20%3D%20%5Bx0-sizeX%2F2%2C%20x0%2BsizeX%2F2%5D%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20my%20%3D%20%5By0-sizeY%2F2%2C%20y0%2BsizeY%2F2%5D%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20render()%3B%0A%20%20%20%20%20%20%20%20%7D%3B%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20var%20getMousePos%20%3D%20function(evt)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20rect%20%3D%20canvas.getBoundingClientRect()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x%3A%20evt.clientX%20-%20rect.left%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%3A%20evt.clientY%20-%20rect.top%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />
</head>
<body onload="setupCanvas();">
    
<div style="margin:0 auto;width:505px;padding-top:10px;">

<div>Color Scheme:
        <select id="scheme">
<option value="1" selected>Color Scheme 1</option>
<option value="2">Color Scheme 2</option>
<option value="3">Color Scheme 3</option>
<option value="4">Color Scheme 4</option>
<option value="5">Color Scheme 5</option>
<option value="6">Color Scheme 6</option>
        </select>
        <input type="button" value="Render" id="render_btn" onclick="render()"/>
        Scroll your mouse on the canvas to zoomin/zoomout
        </div>

        <canvas style="border:1px solid;" id="thecanvas" width="500" height="500"></canvas>
    </div>

</body>
</html>