An Introduction to the Raphael JS Library

An Introduction to the Raphael JS Library

Raphael JS is a lightweight and super-sexy JavaScript framework that allows you to draw vector graphics in your browser! In this tutorial, I will introduce you to some basic drawing functionality, take a look at animation, provide DOM access and finally finish off by creating a cool widget for your site…

Tutorial Details

  • Framework: Raphael JS
  • Version: 1.0
  • Difficulty: Beginner to Intermediate
  • Estimated Completion Time: 30 minutes

1. Getting Set Up

Let’s get started by downloading the Raphael JS framework from here. At the top right of the page, you’ll see
compressed and uncompressed copies of Raphael version 1.0. I’d recommend you grab yourself a copy of the uncompressed source for the time being – this
way you can have a peek at the source and see what extra edge you can get on the documentation.

With that downloaded, let’s set up a simple HTML document called index.htm and include Raphael in it. We also include our_script.js, which is where
we’ll write our own JavaScript, and in the body of the document we create a minimally styled div with ID canvas_container, which will act as a
container for our drawings.

<html>
    <head>
        <title>Raphael Play</title>
        <script type="text/javascript" src="path/to/raphael.js"></script>
        <script type="text/javascript" src="path/to/our_script.js"></script>
        <style type="text/css">
            #canvas_container {
                width: 500px;
                border: 1px solid #aaa;
            }
        </style>
    </head>
    <body>
        <div id="canvas_container"></div>
    </body>
</html>
        

N.B. The first stable release of version 1.0 was only made available on the 7th October 2009, so it’s pretty new. It makes one very important change to the way
you draw paths, so if you’re using an earlier version of Raphael, make sure you upgrade and check out the documentation on effecting backwards compatibility.

2. Creating our Drawing Canvas

When we draw with Raphael, we do so onto a canvas. This canvas, which we’ll reference in a variable called ‘paper’, is created using the
Raphael() object. We always specify the width and height of the canvas, but have the option of also specifying either a) the absolute position of
the canvas relative to the viewport, or b) an element ‘container’ that the canvas is drawn inside.

var paper = new Raphael(x, y, width, height); //option (a)
var paper = new Raphael(element, width, height); //option (b)
        

I generally prefer the latter method (b), since we usually know where our divs are. In our_script.js, let’s wait for the DOM to load and then create a 500px by 500px
canvas inside our canvas_container div:

window.onload = function() {
    var paper = new Raphael(document.getElementById('canvas_container'), 500, 500);
}
        

All our drawing methods will now be bound to the paper variable.

3. Built-in Shapes

Now that we have our canvas, let’s draw some shapes onto it. The origin, that is, the x = 0, y = 0 point, is at the top-left corner of
our canvas. This means that any x, y coordinates we specify in our methods are relative to this point.

First off, a circle. Modify our_script.js to look like this:

window.onload = function() {
    var paper = new Raphael(document.getElementById('canvas_container'), 500, 500);
    var circle = paper.circle(100, 100, 80);
}
        

This will draw a circle with a radius of 80px with its center placed at x = 100, y = 100. We can draw as many circles as we like and we don’t have to
reference them in a variable:

for(var i = 0; i < 5; i+=1) {
    var multiplier = i*5;
    paper.circle(250 + (2*multiplier), 100 + multiplier, 50 - multiplier);
}
        

Next, let's draw a rectangle. We do this using the rect() method, which takes as parameters: the x and y coordinates of the rectangle's top-left corner and the
rectangle's desired width and height.

var rectangle = paper.rect(200, 200, 250, 100);
        

Finally, we'll draw an ellipse. Its parameters are the same as the circle, i.e. x, y, radius, except that we can specify x and y radii specifically.

var ellipse = paper.ellipse(200, 400, 100, 50);
        

This will draw an ellipse with x-radius = 100, y-radius = 50 at x = 200, y = 400. Our our_script.js file should now look like this:

window.onload = function() {
    var paper = new Raphael(document.getElementById('canvas_container'), 500, 500);
    var circle = paper.circle(100, 100, 80);
    for(var i = 0; i < 5; i+=1) {
        var multiplier = i*5;
        paper.circle(250 + (2*multiplier), 100 + multiplier, 50 - multiplier)
    }
    var rectangle = paper.rect(200, 200, 250, 100);
    var ellipse = paper.ellipse(200, 400, 100, 50);

}
        

If we now open up index.htm in our browser, we should get a bunch of shape drawings:

Example Here

4. Drawing Paths

While the built-in shapes are handy to have, it is paths that offer us true drawing flexibility.
When drawing paths, it helps to think of an imaginary cursor or pen-point pressed against the screen. When we create our canvas, the cursor is rooted to the
top-left corner. The first thing we should do, then, is
lift up our cursor or pen-point and move it to a spacious region in which we can draw.

As an example, let's move our cursor to the centre of our canvas. That is, let's move it 250px in the x-direction and move it 250px in the y-direction.

We do this using a so-called path string.

A path string is a string comprised of 'action' commands and numeric values corresponding to the command. We move our cursor to x = 250, y = 250 using the following
string:

"M 250 250"
        

'M' means we want to move without drawing and is followed by x and y canvas co-ordinates.

Now that our cursor is where we want it, let's draw a line relative to this point using the lower-case 'L' command, 'l'.

"M 250 250 l 0 -50"
        

This will draw a line upwards 50px in the y-direction. Let's write a path string that will draw a tetris tetronimo:

"M 250 250 l 0 -50 l -50 0 l 0 -50 l -50 0 l 0 50 l -50 0 l 0 50 z"
        

The 'z' command signifies the path closing - it will join a line from wherever we are to the point specified by our initial 'M' command.

We tell Raphael to actually draw this path using the path() method. Modify our_script.js to look like this:

window.onload = function() {
    var paper = new Raphael(document.getElementById('canvas_container'), 500, 500);
    var tetronimo = paper.path("M 250 250 l 0 -50 l -50 0 l 0 -50 l -50 0 l 0 50 l -50 0 l 0 50 z");
}
        

If you load up index.htm, you should now see a tetronimo like this:

Path strings can become incredibly (brilliantly) complex using curve and arc commands. Full coverage of paths can be found at the
SVG Path specification page.

5. Attribute Styling

Our tetris tetronimo, whilst wonderful, is not very aesthetically pleasing. We'll fix that using the attr() method.

The attr() method takes an object consisting of various property-value pairs as its parameter. Since we stored a reference to our tetronimo in the variable tetronimo, we can take this variable and add the attr() method to it. We could equally well
chain the attr() method to the path() method, but let's keep things sane for the time being. I'll demonstrate the use of attr() by example:

window.onload = function() {
    var paper = new Raphael(document.getElementById('canvas_container'), 500, 500);
    var tetronimo = paper.path("M 250 250 l 0 -50 l -50 0 l 0 -50 l -50 0 l 0 50 l -50 0 l 0 50 z");

    tetronimo.attr({fill: '#9cf', stroke: '#ddd', 'stroke-width': 5});
}
        

produces this:

window.onload = function() {
    var paper = new Raphael(document.getElementById('canvas_container'), 500, 500);
    var tetronimo = paper.path("M 250 250 l 0 -50 l -50 0 l 0 -50 l -50 0 l 0 50 l -50 0 l 0 50 z");

    tetronimo.attr(
        {
            gradient: '90-#526c7a-#64a0c1',
            stroke: '#3b4449',
            'stroke-width': 10,
            'stroke-linejoin': 'round',
            rotation: -90
        }
    );
}
        

produces this:

The Raphael documentation is pretty extensive when it comes to the attr() method.
Have a play around with the various object property-value combinations.

6. Animation

The animate() method in Raphael is really, really good. It allows us to animate our drawings in a jQuery-esque manner, animating
the attributes we supply it over some period of time with an optional easing.

Let's rotate our most recent tetronimo by 360 degrees. The rotation
attribute is absolute, so this should take it one full rotation and bring it back to its un-rotated state.

window.onload = function() {
    var paper = new Raphael(document.getElementById('canvas_container'), 500, 500);
    var tetronimo = paper.path("M 250 250 l 0 -50 l -50 0 l 0 -50 l -50 0 l 0 50 l -50 0 l 0 50 z");
    tetronimo.attr(
        {
            gradient: '90-#526c7a-#64a0c1',
            stroke: '#3b4449',
            'stroke-width': 10,
            'stroke-linejoin': 'round',
            rotation: -90
        }
    );

    tetronimo.animate({rotation: 360}, 2000, 'bounce');
}
        

The animation takes place over 2 seconds (2000 milliseconds) and is told to ease into its final state with a 'bounce'.

Example here.

We can also supply a callback function as an argument. This callback function is invoked after the animation finishes. The following example
will animate the tetronimo's rotation and stroke-width and then reset itself with another animation in the callback function.

tetronimo.animate({rotation: 360, 'stroke-width': 1}, 2000, 'bounce', function() {
    /* callback after original animation finishes */
    this.animate({
        rotation: -90,
        stroke: '#3b4449',
        'stroke-width': 10
    }, 1000);
});
        

The this keyword references the original tetronimo from within the callback function.

Example here.

Animating Paths

Being a bit of a code geek, I rarely ever got past drawing simple shapes in Flash. But one thing I liked playing with was shape tweening. Well,
Raphael goes some way to emulating shape tweening by specifying a path string in the animate() method.

Another tetronimo, the Z tetronimo in Tetris, has the following path string,

"M 250 250 l 0 -50 l -50 0 l 0 -50 l -100 0 l 0 50 l 50 0 l 0 50 z"

and it looks like this:

Now, using our original tetronimo with minimal attribute styling, i'm going to specify the new path string in our animate() method.

tetronimo.attr(
    {
        stroke: 'none',
        fill: 'blue'
    }
);

tetronimo.animate({
    path: "M 250 250 l 0 -50 l -50 0 l 0 -50 l -100 0 l 0 50 l 50 0 l 0 50 z"
}, 5000, 'elastic');
        

You should see our original tetronimo morph into our new one. The effect is made all the more pronounced by specifying 'elastic' as the easing type.

Example here.

7. Dom Accessibility

If we want to get access to our elements as DOM elements, we can do so with some ease. This is thanks to the node property. Using this, we can
add event handlers to our drawings, which i'll proceed to show you.

Let's start by drawing a circle in our our_script.js file.

window.onload = function() {
        var paper = new Raphael(document.getElementById('canvas_container'), 500, 500);

        var circ = paper.circle(250, 250, 40);
        circ.attr({fill: '#000', stroke: 'none'});
}
        

Now, let's add the text, 'Bye Bye Circle!' so that its center point is at the same point as our circle center.

var text = paper.text(250, 250, 'Bye Bye Circle!')
text.attr({opacity: 0, 'font-size': 12}).toBack();
        

I have set the opacity to 0 so that it is initially hidden. Notice the chaining of the toBack() method. This places the text behind all other
canvas drawing elements (similarly, toFront() brings elements to the very front of our canvas).

Now, let's add a mouseover event handler to our circle using the node property. We will set the cursor style to 'pointer'.

circ.node.onmouseover = function() {
    this.style.cursor = 'pointer';
}
        

What this actually does is set the style property of the <circle> object in our document. Our document looks like this:

<circle cx="250.5" cy="250.5" r="40" fill="#000000" stroke="none" style="fill: #000000; stroke: none; cursor: pointer">
</circle>
        

Now, let's finally add an onclick event handler to our circle:

circ.node.onclick = function() {
    text.animate({opacity: 1}, 2000);
    circ.animate({opacity: 0}, 2000, function() {
        this.remove();
    });
}
        

When the circle is clicked, the text we referenced in the variable text is animated to full opacity over 2 seconds. The circle itself is animated
to 0 opacity over the same time period. We also include a callback function in the circle's animate method. This removes the
the circle element from our document once the animation has finished, since whilst the circle has 0 opacity, it is still clickable until removed.

Example here.

8. Let's Build a Widget

Finally, let's pull together what we've learned and build a pretty little Mood Meter. Basically, you will select a mood value between 1 and 5, 1 being 'rubbish' and
5 being 'positvely manic', and Raphael will create a nice representation of this.

View the widget here

Begin by modifying our_script.js to look like this:

window.onload = function() {
    var paper = new Raphael(document.getElementById('canvas_container'), 500, 500);
    var circ = paper.circle(250, 250, 20).attr({fill: '#000'});
    var mood_text = paper.text(250, 250, 'My\nMood').attr({fill: '#fff'});
}
        

This creates a circle of radius 20px at the center of our canvas and some text on top of the circle saying 'My Mood'. 'Mood' is placed on a new line using
'\n'.

Next, let's create some custom information corresponding to our moods and choose which mood we're in.

moods = ['Rubbish', 'Not Good', 'OK', 'Smily', 'Positively Manic'];
colors = ['#cc0000', '#a97e22', '#9f9136', '#7c9a2d', '#3a9a2d'];

//pick a mood between 1 and 5, 1 being rubbish and 5 being positively manic
var my_mood = 1;
        

The text description of our mood is stored in an array called 'moods' and the color corresponding to this mood is stored in an array called 'colors'.
The chosen mood, a value between 1 and 5, is stored in the variable my_mood.

Now let's create a function called show_mood. When invoked, this function will display our mood circles (the colored circles) and the text corresponding to this mood.

function show_mood() {

    for(var i = 0; i < my_mood; i+=1) {
        (function(i) {
            setTimeout(function() {
                paper.circle(250, 250, 20).attr({
                    stroke: 'none',
                    fill: colors[my_mood - 1]
                }).animate({translation: '0 ' + (-42 * (i+1))}, 2000, 'bounce').toBack();
            }, 50*i);
        })(i);
    }
    paper.text(250, 300, moods[my_mood - 1]).attr({fill: colors[my_mood - 1]});

    mood_text.node.onclick = function() {
        return false;
    }
    circ.node.onclick = function() {
        return false;
    }

}
        

In show_mood(), we have a loop that iterates as many times as the value of my_mood. Inside this loop is a self-executing anonymous function. This is necessary so that
we have access to the variable i at each stage of the iteration. Inside the self-executing function, we create a timeout - every 50*i seconds, a circle
is created at the point of our original circle. Each circle is then translated over 2 seconds to 0px in x and some multiple of -42px in y. We make sure to place
each successive circle at the back of the canvas. Note that the circles are filled according to the color in the colors array, determined by my_mood.

show_mood() is also responsible for the display of our mood text which uses my_mood to pick the corresponding mood from the moods_array.

show_mood() then finally get rid of any onclick event handlers assigned to the original text and circle we placed at the center of the canvas. This prevents
the re-drawing of moods circles.

Finally, let's assign onclick event handlers to the center circle and 'My Mood' text. I assign event handlers to both elements so that clicking on either
the text or circle has the effect of calling show_mood().

circ.node.onclick = show_mood;
mood_text.node.onclick = show_mood;

Conclusion

Well, that's it! You should now have a sound platform on which to base your explorations into the Raphael JS framework. Most importantly, I hope
you're now eager to delve into Raphael JS and concoct some beautiful browser drawings and widgets. Don't forget to follow me on Twitter, and share your creations.


Add Comment

Discussion 64 Comments

  1. Holy cow, this is great!

  2. awake says:

    No offense but nettuts has way better content than tuts+ for its section.

    It will be nice if tuts+ has more tutorials for front-end engineers and developers.

    Don’t get me wrong, tuts+ is probably great for designers, but there’s not much useful stuff for developers there.

    • w1sh says:

      Thanks Yoosuf. I totally see how a listing of IDEs are the related to this post.

      Oh! Is that your blog? Cool! I didn’t know you wrote such in-depth analysis on everything related to WEB-DEVELOPMENT and WEB-DESIGN. Maybe you should learn Javascript here and buy your own house now.

  3. OMG! I never knew about this library, thanks for posting!

  4. yann says:

    that looks AWESOME! gotta try it out later on

  5. Shane says:

    I remembered getting excited about SVG a while back. Unfortunately, IE doesn’t support it, so this is a definite alternative, albeit not quite as nice.

    Thanks for the heads-up.

    • Ben says:

      Ugh what a buzzkill. I was so stoked about this until I fired up IE8 to verify your comment.

      Huge fail.

      • Dmitry says:

        What fail you are talking about?

      • w1sh says:

        The fact that IE won’t render SVG directly. You have to use a js library to do it or the users have to download a plugin, else it fails.

        http://en.wikipedia.org/wiki/Scalable_Vector_Graphics

        39.6% of users use IE6, 7, or 8. And 5% of those don’t have JS turned on. That’s a huge market (2%?) of really prospective (people who wouldn’t know how to order from an e-store anyway) visitors to just give the middle finger to.

        Oh wait, no it’s not. :\

        If you’re a total freak about it and really want to get an extra 1% of visitors, Flash offers 99% of the market.

        Sheesh Dmitry, you’d think -you- of all people would know that. :)

        http://www.w3schools.com/browsers/browsers_stats.asp
        http://www.adobe.com/products/player_census/flashplayer/

         

      • Author

        But Ben said ‘huge fail’ — the VML implemented in IE8 is by no means a huge fail. IE8 is fail, but that’s by the by.

      • Ben says:

        The fail I was refering to is the fact that nothing built with this library will render in IE. Unless there’s something I’m missing…

      • This library works *even* in IE6.

        I tried the demos in IE6, IE7 and IE8. It works in all these browsers. As advertised! The home page of Raphaël says…

        Raphaël currently supports Firefox 3.0+, Safari 3.0+, Opera 9.5+ and Internet Explorer 6.0+.

        No buzz kill.

        Have fun.

        Great tutorial… thanks again.

      • Yes, Ben, you’re missing that everything in Raphaël works in IE 6, 7 & 8 just as it does in Safari or FF etc. Maybe a *little* slower, but the rendering is the same.

        The library uses VML for IE and only provides SVG functionality that it can replicate in VML.

        It’s not intended to be a full implementation of SVG, but a complete and consistent cross-brower/platform vector drawing library.

        Pretty cool, I think :)

  6. Your Name says:

    Cool but what is faster a image or using what you said (Javascript)?
    Thank’s for the tutorial!

  7. Mark Sinkinson says:

    I actually need something like this!

    Fantastic

  8. I took a look at the Raphael JS website, and the demos are AWESOME. I’m amazed at what this framework can do.

  9. Dmitry says:

    You don’t have to use new operator with Raphael function, and you could pass element id as a first argument. I mean this
    var paper = new Raphael(document.getElementById(‘canvas_container’), 500, 500);
    could be rewritten like this:
    var paper = Raphael(“canvas_container”, 500, 500);

  10. Adrian says:

    This is probably how http://shaun.in/man/ works.

    • Shane says:

      Interesting – hadn’t seen that before, but no, look at the object tag, and in particular, its type attribute: image/svg+xml.

      That’ll be SVG then :)

  11. rn says:

    THX, this is awesome!!!
    OpenGL support is coming soon ;)

  12. Marlou says:

    This is so awesome!
    Thank you so much, great tutorial.

    (Holy crap, it works in IE6!)

  13. Author

    In the context of this all being really really cool, I thought i’d point you to the gRaphael plugin:

    http://g.raphaeljs.com/

    Cool, and gives you some idea as to one possible scope of implementation.

    Also, the demos at raphaeljs.com are a must for those of you that haven’t already seen them.

  14. Nori Silverrage says:

    Love it. I was actually just trying to figure out how to do something like this.

  15. Jorik says:

    Today v.1.1 released with some bug fixes!

  16. Scott says:

    I think this sees pretty col but I’m not sure if it is easier than just using images?

  17. WOW! This is like the drawing tools Adobe Flash has, and its pretty slick!!!

  18. Nanda says:

    one word, awesome.

  19. adrusi says:

    any way I could access the svgs with jQuery?

  20. nathalie says:

    can’t get href to work

  21. Jhon says:

    krokee.com is using this library to annotate. Enjoy !

  22. Ali A. Akbar says:

    Doesn’t even work. Course. Nettuts has gone to hell.

  23. Jan says:

    The links to examples aren’t working… but otherwise a great tutorial

  24. rossisen says:

    Is there any way to make the text non-selectable?

  25. Waqas says:

    HI, I am designing MS paint using raphael.js. Can someone help me how to draw a circle with mouse in anywhere in the canvas, just like MS Paint. please i really need help. Thanks

  26. Simon Taylor says:

    A really good tutorial, well done.

  27. Gregi says:

    Thanks Damian, that’s one of the coolest tuts i’ve read. Bless,
    Greg

  28. moohinho says:

    Excellent intro to Raphael, thanks.

  29. sv says:

    great tutorial.
    Is there a way to multiply along a path? like your repeated circles but along a path.
    thanks

  30. Spadam says:

    Thank you so much for this excellent tutorial, very informative and inspiring. I used the techniques to create a bird’s eye view map for our school’s website:

    http://www.woodsetton.dudley.sch.uk/SchoolMap.asp

  31. Deno says:

    Hi,
    this introduction and the whole framework is great. Only there are missing a very few things. For example the built-in scene graph. Can someone tell me an idea how a workaround could look like?
    For example, I have my node (paper.rect) and a text (paper.text at the same position). And I want the text always to change position when the node does. It seems, I can not just extend the node with node.label = paper.text(…) and then when changing the position of the node, also change the position of text.label. :-(
    Is there an idea how it could be done nicely?
    Thank you for answers! :-)

  32. StudAss says:

    Hi,
    I am wondering how to add an eventhandler to the canvas. I want an event to be fired, when clicking on the background of the canvas/”paper”.
    I tried something like this,
    canvas = Raphael(“canvasContainer”, 1000, 1000);
    canvas.click(function(){
    document.getElementById(‘inner-details’).innerHTML += “canvas clicked” + “;
    });

    But the script is not further executed at this point.
    Is there an idea/trick, how to get this done somehow?

    Thank you!

  33. StudAss says:

    (I also tried something like
    canvas.onclick = function(){…} or
    canvas.node.onclick = function(){…}

    it was the same result…) :-(

  34. Samsol says:

    I am thankful to you who inspire to study raphael lib and to develop this 3d demo using raphael lib. Check out on-
    http://samsol3diphone.blogspot.com/p/3d-demo-in-javascript.html

    Thanx once again.

  35. Aldian says:

    Congratulations, this tutorial is just wonderful.

    Thank you very much!

  36. dEvElOpEr says:

    Hi,
    I have a question.
    I have Raphael-elements on my canvas and some events for dragging and dropping elements or make them wider or thinner. But:
    When I drag the mouse, it happens sometimes (quite often but not always) that I get a document-symbol on my cursor and for example the next mouse-up-event for dropping the element is not fired.
    And when there had to be a mouse-up-event after a mouse-down-event for keeping the consistence, there comes chaos.
    Is this a common problem? And has anyone an advice how to deal with this? There is no error written on the error console. And I do not really know what to do against this.

    Many Thanks in advance!

  37. dEvElOpEr says:

    Hi,
    I had to fill in the div-element in the index.html, wherein I drew the canvas with Javascript. Then it can not bei no more, that a div is dragged accidentally.

    Regards,

    D

  38. Kaleb says:

    The examples’ URLs are broken.

  39. Aman says:

    Thank you for the tutorial. Its exactly what a quick introduction should be.

    NOTE –
    Rotate and translate are no longer(as of version 2.0.0) attributes of an element. They are functions that can be called on elements.

    It’d great if you could update your tutorial with that.

    List of complete attributes can be found in the documentation here:
    http://raphaeljs.com/reference.html#Element.attr

  40. Zech says:

    All the links to demo’s appear to be broken.

  41. KM says:

    Thanks for this wonderful tutorial – it gave me a good head-start with Raphael JS. However, I found something (including links to demos as others have pointed out) not working as the tutorial states.

    The “rotation” property apparently isn’t there any more (v 2.0.1) in the list of properties in Element.attr [http://raphaeljs.com/reference.html#Element.attr]. However the same effect could be achieved using Element.transform [http://raphaeljs.com/reference.html#Element.transform].

    replaced “rotation: -90″ with “transform: r-90″ and it worked as expected.

  42. Zhu J.C. says:

    Probably the syntax has changed in v2.x, for the very beginners like me, to make the ‘show mood’ example work, change the following text
    animate({transform: “t0,” + (-42 * (i+1))}, 2000, ‘bounce’).toBack();
    //animate({translation: ’0 ‘ + (-42 * (i+1))}, 2000, ‘bounce’).toBack();

    REF.
    * stackoverflow question,
    http://stackoverflow.com/questions/8112646/raphael-js-2-0-animate-translation-with-variable-inconsistency-noob

  43. Saminda says:

    Thanks. This is a really good introduction to Raphael.

  44. Anees says:

    this works for step 6 Animation

    shape.attr(
    {
    fill:’#9cf’,
    stroke:’#ddd’,
    ‘stroke-width’:4,
    ‘stroke-linejoin’:’round’,
    transform: “r-90″
    }
    );

    shape.animate({transform:”r360″}, 2000, ‘bounce’);

    change rotate to transform.

    thanks

  45. Gerard says:

    Wow, I was really impressed with the Raphael library but was looking for a good tutorial. Thank you for this, this stuff is awesome!

Add a Comment

To add a code snippet to your comment, please wrap your code like so: <pre name="code" class="html">YOUR CODE</pre>. You can replace the class name with "js," "css," "sql," or "php." If there are any "<" or ">" within your code, please search and replace them with: &lt; and &gt; respectively.