Water ripple FX with Canvas and Javascript

It’s not new and it was done many, many times with Java Applets: the Water Ripple effect! Yeay! I created one some years ago since I just like to play with it so much. Nowadays we have the HTML5 canvas but also fast javascript engines . I still wanted to experiment with the HTML5 canvas anyway so I decided to port my applet to a canvas.

Check the Water Ripple Effect here!

In this article I will show the effect, explain how the effect works and also how you can reuse the highly configurable javascript classes. You could use it for nice cheesy banner effects or just to impress your site visitors ;) . Anyway… lets check it out!

The water ripple canvas

It’s best to check this canvas effect on a browser that supports HTML5 canvas (like Google Chrome, Safari, Firefox or IE9)! I recommend to view it on Firefox 4. At time of writing it’s by far the best performing browser for this algorithm. For you people that found this blog with another browser, here’s a screen shot image:

Water FX canvas example

The water ripple effect mimics a top view on water with water rippling and refraction of light through the water. On my implementation you can use your mouse like it’s your finger that touches the water. Pressing the mouse button gives you a bigger ‘finger’. And… there’s no need for performing your rain dance anymore. Just adjust the “Raindrops per second” slider to make raindrops fall on the canvas. If you’re like me, you can spend hours moving your mouse around on the canvas with big eyes and a big smile on your face ;) .

Update (March 8th, 2011): I implemented the use of requestAnimationFrame (Read more about it in this article by Paul Irish). It triggers a callback function just like setTimeout would. But now it’s not the time but your browser telling the best time to render the water pixels on the canvas. Also note the ‘FPS Canvas’ for going up after you switched to another tab and back to the water canvas. The browser knows it’s no use redrawing the canvas when no-one is watching it and so preventing your CPU/GPU fans from going mental for nothing ;) .

Pimp my site… (with a swimming pool)

How should you add the effect to your site? First of all, the code and the best/preferred way to implement it described here may be updated at any time in the future.  So check back this section when you decide to use the effect in the future. Next, the code will be officially open sourced and will be put on Github when I have time to do this. It’s best to use that code from then on.

To use the code you should put just one javascript file on your web server: watercanvas.js. Then you should include this script in your HTML file’s <head> code:

<script src="watercanvas.js"></script>

Now you need to create some location (a <div>) where the WaterCanvas can insert it’s to be created canvas element. Place this div at the location between the <body> tags where you want the canvas to show up.

...
<div id="waterHolder"></div>
...

Give it an ID so we are able to point to it in javascript in the following step. We are now ready to create and init the necessary objects. In the <head> you should now add a <script> section. Add the following javascript code in it:

<script>
var width = 350;
var height = 275;

function init(){
	var waterModel = new WaterModel(width, height, {
			resolution:2.0,
			interpolate:false,
			damping:0.985,
			clipping:5,
			evolveThreshold:0.05,
			maxFps:30,
			showStats:true
		});
	var waterCanvas = new WaterCanvas(width, height,
		"waterHolder", waterModel, {
			backgroundImageUrl:"images/yourimage.jpg",
			lightRefraction:9.0,
			lightReflection:0.1,
			maxFps:20,
			showStats:true
		});
}
</script>

We created an init function which we will soon call at the document loaded event. When the init function is called, two javascript objects will be created:

  • A WaterModel object:  A model object which is responsible for holding, manipulating and evolving the water ripple data. The model does not need to use the same amount of pixels as the canvas. You should use the same width and hight as for the WaterCanvas but you can set a resolution and use bilinear interpolation.
  • A WaterCanvas object: A view object which is responsible for applying the water ripple data state to an image and therefor is also responsible for the reflection and refraction effects.

See the javascript jsdoc comments in the javascript files for descriptions of all parameters and some more explanation. In the example you can use the sliders to update settings in the models and experiment with it. It’s a great way to find the settings you want to use.

To finish it we need to call the init method when the DOM is loaded so we are sure the div we created before is available (If you know how to work with javascript and DOM loading timings, you can do this in better/faster ways. For the sake of simplicity I’m using the plain old body onload here.):

<body onload="init()">

Now upload all files to your server and open the HTML file  in your browser… Wait, nothing is happening!? Of course, we need to “touch” the WaterModel to see the effect in action:

<script>
...
var finger = [
	[0.5, 1.0, 0.5],
	[1.0, 1.0, 1.0],
	[0.5, 1.0, 0.5]
];

waterModel.touchWater(100, 100, 1.5, finger);
</script>

The touchWater function needs an X and Y position, a pressure multiplier and a 2D array pattern which represents a “finger” or “raindrop”. This is actually the basis of how to use the Water Canvas… Ok, a little more insight in the code above:

You need to create a 2D array which the touchWater function is using to apply to the WaterModel. This is powerful since you can apply pressure to the WaterModel in any shape you like! See my example for how I use a util function which is also in watercanvas.js (util functions are at the bottom of the file). You can use it to create a 2D array from a (small) canvas which you can draw to in gray scale values to create your own custom shapes.

You can also make your mouse pointer touch the WaterModel. You need to use mouse EventListeners to track your mouse. Again, I created an out-of-the-box util function for you. Check the code of my example to find out how to do this.

The water ripple FX algorithm and javascript

So what’s the trick? What does make this water effect work?

There’s actually a very good tutorial by Hugo Elias about the functionality of the WaterModel which I used when I create my first implementation in a Java Applet. So I won’t go to deep into that. In short; for every position in a 2D array the algorithm looks in the surrounding 2D array positions for the float values, then applies some devisions, et voila, there’s a new value for the current position. You need to use two 2D arrays. One for the current state and one for the new state to calculate. All newly calculated positions will be put in a second 2D array buffer. After all positions are rendered the buffers will be swapped before the next calculation round starts.

The WaterCanvas uses the state of the data in the WaterModel at a frame render moment. It reads the positions of the WaterModel’s 2D array buffer and uses each value to manipulate the pixels of the WaterCanvas’s canvas pixels. There are two different things I applied to these pixels;

  • reflection of light on the water
  • refraction of light through the water

For reflection, for every pixel of the canvas we get a value from the WaterModel. When the WaterModel returns a positive value, we’ll make the pixel color a little lighter by the amount of the value, when negative, a little darker by the amount of the value. Refraction is done in almost the same way. When the value is positive get the pixel of the current position with the positive amount added. For a negative value automatically the current position minus the absolute value is used. This is in short how it works. Wanna know more? Check out the code!

Splish splash… The conclusion

So far this canvas experiment on realtime pixel manipulation works. But, when compared to some Java Applets I should conclude Javascript is just not as fast. On the other hand, canvas’s start faster then Java Applets without Java splash screens (what’s in a word ;) ). When you use the Water Canvas you should keep the canvas as small as possible to be sure it performs. Set the max FPS’s to the minimum you find to be OK.

There are some enhancements I may try at some time like using an HTML5 WebWorker for the WaterModel and/or using some WebGL hardware performance optimizations. At this point I really don’t know if WebGL can be used at all for this effect.

I should also take some time to make the Water Canvas degrade gracefuly on old browsers. Currently you will get bothered with an anoying javascript alert when the browser doesn’t support HTML5 canvas. Also, it should be possible to point this script to an <img> tag or another <canvas> tag, where the WaterCanvas class constructor will convert it to a Water Canvas for you.

Let me know what you think of the Water Ripple Canvas or how I can improve the performance of it. OK, I’m off for a swim…

Tags

Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

36 Comments

  1. Posted November 5, 2010 at 8:17 PM | Permalink

    Interesting, but your ripple simulation runs very slow even in a browser with a fast JavaScript engine, like Chrome or Opera. Check out this HTML5 Dynamic Water Effect, which runs significantly faster, even using the same water model method.

  2. Almeros
    Posted November 7, 2010 at 6:47 PM | Permalink

    Hi Nicolas, I agree (although the implementation you mention has probably been updated since I’ve seen this one being slower before). The problem with my implementation is probably due to implementing this effect in an Object Oriented (almost MVC) way. I’ve seen an online presentation from Thomas Fuchs recently on high performance Javascript. He has many tips on improving speed which I might one day use to increese the speed. A key for speed is to keep all processing in one loop as much as possible. I will make the code less readable but probably faster.

  3. Posted November 9, 2010 at 12:38 AM | Permalink

    Hi Almeros,

    The implementation I pointed to was not updated since its original. I think this was the first HTML5 water ripple effect created. That aside, my only point is that this problem only becomes interesting once you consider the efficiency aspect, even if that means you have to forgo a “perfect” design pattern.

  4. Almeros
    Posted November 9, 2010 at 11:27 PM | Permalink

    My point exactly Nicholas! Congratulations on creating the first HTML5 water ripple ;P

    I also found this implementation from chikuyonok. I personally think it’s the best one out there right now!

  5. Wouter
    Posted February 18, 2011 at 12:44 AM | Permalink

    Hi Almer ..

    Nice thing, my CPU gets stuck on 12.5% (1/8cores). But is see you are already thinking of a more subtle solution ;-)

  6. Almeros
    Posted February 20, 2011 at 1:39 PM | Permalink

    Hi Wouter, thanks for your comment. Nice observation of the CPU core usage!

    Now there’s a problem in that you can’t really tell Javascript how and which cores to use, neither ask how many cores there are available on a client. That’s something the browser’s Javascript engine decides/implements. To solve this I might try to use multiple HTML5′s Web workers (but this effect won’t be working in the upcoming IE9 when using WebWorkers, but then again, the Canvas in IE9 preview shows bugs with this effect anyway ;) ).

    What browser did you use? I might try to use multiple cores next time I’m working on this effect!

  7. Diego Crusius
    Posted March 22, 2011 at 5:16 AM | Permalink

    Just wanted to mention that, when using small dimensions your app is quite fast and useful. I knoow its not optimal (like using in a giant and beautyful background or something) and probably not the usage you expected but its a good start.

    Maybe if disabling some features (like clicking on it) it will make it faster a bit? I am having a similar problem with a jquery app I’m using for 3d effects, but its eating my pc quite a bit, so I’m considering purchasing one of those Thomas Fuch’s books because it seem to be very handy.

    Thank you for all the work you had.

  8. Almeros
    Posted May 16, 2011 at 5:06 PM | Permalink

    Hi Diego Crusius, thanks for your comment and sorry for replying just now.

    If you’re creating 3D effects, be sure to also check out Web-GL for creating 3D effects via your graphics card’s GPU. Nothing will beat that in performance! :)
    I personally could also try and use Web-GL for this 2D effect also. But there’s a lot for me to check out (learn how shaders work) and I should probably refactor the current code in total.

  9. Veen00
    Posted June 29, 2011 at 4:07 PM | Permalink

    Is it possible to get the ripple’s only in a circle?

    i have a image of a cup of coffee.. like to ripple the coffee only.

    thnx!

  10. Almeros
    Posted June 29, 2011 at 5:52 PM | Permalink

    Hi Veen00,

    I quess there’s two ways doing that, an easy way and a hard way (which will be more realistic).

    For the hard way you should alter my algorithm code and create a circular 2d buffer some way. The water waves will bounce of the sides more realisticly. The easy way (go for this one if you want to use it for fun) should be achieved by using the coffee background in the canvas. Now add a transparent .PNG image with the cup and a transparent cutout of the coffee content on top over the canvas. Make the canvas with ripple effect as small as possible arround the circle. That should do it!

    If you created this, please send me a link to your creation!

  11. tim
    Posted September 16, 2011 at 1:48 AM | Permalink

    Hello, this is a very great program:)

    I have a little question. Is it possible to make the rain drop effect for only a second or two? With a smooth fade out.

    Thank you!

    Best regards,
    tim

  12. Almeros
    Posted September 19, 2011 at 1:24 PM | Permalink

    Hi Tim, thanks,

    Sure, actually, when you setup a WaterModel and a WaterCanvas just DON’T add a mouse event listener and DON’T add the rain generator. Only create a ‘finger’ in a 2D array like the code above and perform a touchWater on the WaterModel object on the location where you want the ripple to start. Change the settings to let the ripple fade out in about 2 seconds and you’re done (you can use my example’s sliders to figure out what are the best settings for you).

    Nice to know; when your single ripple is done rippling, the WaterModel and Canvas will stop taking resources on your website, keeping the performance for other javascripts optimal.

    Let me know when you have something online! Have fun!

  13. Luke
    Posted January 29, 2012 at 7:59 PM | Permalink

    I want to use this effect on my website but I am only an artist and the instructions overwhelm me. Is there a simpler step by step available? A lot of the terms make no sense to me. I don’t code my sites but use a program that generates the code (OK stop hissing and booing :) and I can insert code and don’t know where I would insert this stuff.

    Thanks

  14. Almeros
    Posted January 30, 2012 at 5:04 PM | Permalink

    Hi Luke,

    First of all, thanks for your interest. I think you’re right that the story above is a bit nerdy ;) . There isn’t really a simpler step by step unfortunately. What I can give you is the code for the simplest working html with a water canvas.

    Put it all on a webserver (unzipped ofcourse) and check it out via your webserver’s URL…

    By checking the index.html in a browser from your desktop (via file:// instead of http://) the background.jpg image won’t be read into the canvas due to the Same Origin Policy (security) the canvas element uses (HTML5 spec). There are ways to disable this in your browser though. Google for it.

    You can fiddle with the settings in index.html now and/or change the background image, until you like it!

    Also, please understand that this effect is taking quite a lot of browser resources. Don’t expect it to perform full screen (or even half) on your webpage or something! Putting it on a site’s logo should however work fine on modern browsers.

  15. Timmy
    Posted June 4, 2012 at 12:58 AM | Permalink

    Thumbs up for your water canvas.

    I’m sorry this might sound as a easy step for most, but I have just started.
    Got the first one to work, but how do I get a second water canvas on the same page.?

    Many thanks in advance.

  16. Shanoir
    Posted September 21, 2012 at 3:55 PM | Permalink

    Hello !
    I just wanted to congatulate you on your work, it is quite interresting !
    I have a question on your script, is it possible not to use a background image, but an transaprent image instead. I am using a phpbb forum, and i would like to use your script on my logo.
    What need to be changed in your script to make it work properly ?
    Thanks for your time…

  17. sash
    Posted September 28, 2012 at 12:49 PM | Permalink

    little typing error to correct. search for “”

    apart from that: BEST looking solution!

  18. sash
    Posted September 28, 2012 at 12:50 PM | Permalink

    typing error: “scrip” instead of “script”

  19. Almeros
    Posted October 1, 2012 at 9:37 PM | Permalink

    Thanks Sash, that was the quiz question ;) . Updated it!

  20. Andy
    Posted October 16, 2012 at 5:28 PM | Permalink

    Hi There

    While creating a 3d cube canvas effect I found out how slow manipulating the raw pixels in javascript is, I found a faster way was to draw cropped areas of the source image on the canvas and distort them line by line, this massivily sped up my effect because the canvas drawing is hardware accelerated. Would love to see if your speed increases doing it this way. I think the demo is great.

    Andy

  21. Almeros
    Posted October 17, 2012 at 9:41 AM | Permalink

    Hi Andy, this water effect unfortunately does really need full X by Y pixel manipulation making it a slow algorithm. The only real CPU cycle optimization thing I could do is use requestAnimationFrame. Actually I’ve seen a faster implementation of this algorithm somewhere, it’s less flexible though since it didn’t seem to use a backbuffer (one time XxY instead of two times XxY). But full screen will be to far fetched these days… perhaps WebGL shaders can hardware excellerate these kind of effects.

  22. Amit
    Posted June 14, 2013 at 10:31 AM | Permalink

    how i can put this effect in a lotus pond surrounded by wall which are not shared effect

  23. Almeros
    Posted June 17, 2013 at 12:51 PM | Permalink

    Hi Amit,

    Have you tried putting a div over the canvas with CSS for the lotus image? And with HTML it should be very easy to add ‘wall’ images around the canvas :)

  24. Posted July 13, 2013 at 11:56 PM | Permalink

    Is there A Way To Use The Ripple Effect More Than Once On A Page.
    ie; On A Footer And A Header.
    Thanks.

  25. Almeros
    Posted July 14, 2013 at 10:12 PM | Permalink

    @Shannon,

    Sure, create another div with its own ID and initiate a new waterModel and waterCanvas. In the waterCanvas you should point to the new div’s ID.

    BUT, don’t expect to much from the performance!

  26. jin
    Posted July 23, 2013 at 7:51 AM | Permalink

    var width = 350;
    var width = 275; ?????? not height?

  27. Almeros
    Posted July 23, 2013 at 3:04 PM | Permalink

    @Jin, ofcourse! Will change it right away :)

  28. Lance
    Posted July 29, 2013 at 10:57 PM | Permalink

    Awesome!!! I’m using this now. I wanted to know if there is a way to spread the drops out a little on the x or width coordinates so it doesn’t look so ‘rain drop-ish’ I’m trying to use this over an image of a body of water. sorry just wanted to know, I know its meant for rain drops. Many thanks!!!

  29. Lance
    Posted July 29, 2013 at 10:57 PM | Permalink

    Edit*** just the drop attributes not the array of drops that happen :)

  30. Almeros
    Posted July 30, 2013 at 10:41 AM | Permalink

    Hi Lance, great that you like it! Unfortunately I don’t really understand what you’re trying to achieve. Just know you’re free to fiddle with the code as you want, so why don’t you just try and experiment? Have fun!

  31. Lance
    Posted August 8, 2013 at 4:14 PM | Permalink

    Thanks Almeros! I just wanted to expand the circle by the x value only and not so much by both the X and Y values. I’ll fiddle around. Thanks again for the amazing code! I’ve been searching for js water physics but only have been able to find ripple effects. Anyhow thanks!!!

  32. Lance
    Posted August 8, 2013 at 4:16 PM | Permalink

    Kind of like the webGL water experiment. Where the there is reflection and more horizontal movement. :)

  33. Posted September 8, 2013 at 1:04 AM | Permalink

    Hi there Almeros,

    I loved this tutorial and script and added it to a page. I would love to be able to make it “touch” enabled for iPhone’s and iPads etc.. Do you know how I would do this? My first idea was to create other versions of the even listeners for touch but this did not happen, as when I touch the canvas on an iPhone it moves the whole page. I would love to hear your insights there and anything you see to make this available for all devices! Thank you so much.

    Jamie

  34. Almeros
    Posted October 1, 2013 at 8:54 PM | Permalink

    Hi Jamie, perhaps you could try something with CSS like position:fixed;. For the rest, you should Google it up ;) .

    Also please note this will probably work real slow on iOS devices since Apple seems to throttle javascript execution speed :( .

    Hope this helps a bit!

  35. Posted October 23, 2013 at 11:34 AM | Permalink

    Can the rain drop auto load – without click – or selecting from the scroll?

  36. sagar
    Posted January 2, 2014 at 7:45 AM | Permalink

    Hi,
    Actually i want this effect on my background image but when m increasing the width and height of the image the ripple effect slows down.

    plzz anyone solve my issue

2 Trackbacks

  1. By Moving Background without using flash - DesignersTalk on November 19, 2010 at 10:04 PM

    [...] Water ripple FX with Canvas and Javascript course, the processing will probably kill you on an entire background __________________ [...]

  2. [...] it’s not cubes then it’s ripple effects! Almer’s is pretty good though and he’s made it into a library you can use on your own site. An interesting factoid from his blog post: Firefox 4 runs it best, even better than [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

  • Almeros’ Code Blog

    Welcome to Almeros' Code Blog at code.almeros.com. I'm a Software Engineer living in Groningen, The Netherlands.

    Too many times I found out some coding stuff I really want to share with you all; interesting new coding stuff, problems I found a solution for, things bothering me, etc.

    After many years of getting the knowledge and help from you (other blogging developers), it's time to give something back! That's why I started this blog. I hope you find it useful and that my posts save your day!