15 Aug

Water ripple effect done in HTML 5

I am sure you have seen this effect before, a picture distorted more or less realistically while you move your mouse over it causing the “water” to “ripple”:

water_ripples

In the early 2000s this was done with Java Applets causing a hell of a lot of troubles – from missing and wrong Java versions, over high CPU usage to crashing browsers.
Then Flash came around the corner solving a lot of these problems, but introducing new ones like gaping security holes.
But since all major browsers support HTML 5 Canvas now (Yes, even Internet Explorer starting from IE9) this can be done with Javascript only, which makes things a lot easier and running out of the box (See also: Snow effect done in HTML 5 and Starfield animation done in HTML 5).

Although this effect is nothing more than a gimmick and the practical applications are bare to none, building it was a great way to learn a ton about the Canvas element and its limitations.

The underlying algorithm could not be simpler, i basically copied it from this great explanation: http://freespace.virgin.net/hugo.elias/graphics/x_water.htm

I would never have thought that doing all this calculations on 170.000 pixels 40 times per second would be that efficient (My CPU usage is at only 10-14%), but it shows how much modern Javascript engines optimize your code on runtime.

So it seems that even the Canvas API is missing some features i loved and used in Flash (Like full matrices, direct filtering of image data) you can actually manipulate Canvas image data directly with Javascript and program your own filters and effects to be fast enough on moderately big images.

Enough yapping, here is the code, just copy the files onto a webserver (see notes below as to why): Javascript – Water Ripples
If you are using this on your website and you are a nice person you can show it by giving me a credit (backlink) somewhere on your page, thank you!

Some things to note:

  • Runing the script from your local machine instead from a website will not work in Chrome due to the restrictive same-domain policy of the browser.
  • Make sure the image is loaded from the same (sub)domain as the website containing the script, otherwise you will get security problems.
  • The speed of the waves is mostly defined by the FPS rate and cannot be changed, it is hardwired with the algorithm.
  • It can be that the mouse movement is not working in some browsers, i used a W3C working draft method for getting the mouse position. If you want to use this code in production you have to care for that issue yourself (Maybe with jQuery).
  • Some calculations could be sped up by using bitwise operators instead of multiplication or division, but this needs the dampers to be hardcoded. It seems that with the newest browers there is no longer a difference in speed with bitwise operators over Math methods, so forget this. They have bad caveats you have to care for yourself anyway.

12 thoughts on “Water ripple effect done in HTML 5

  1. hey there – i have been looking for a way to integrate this into a logo image for a while now. I am a TOTAL novice w/ HTML, and our website is a wordpress site. i have tried pasting your code into a “page” in a variety of ways, but none seem to work. wondering if you have any insite – i assumed that the only place to “customize” the code was in the source for the “Original image” at the end:
    i.e. Original image:

    Water effect:

    WAS CHANGED TO
    Original image:

    Water effect:

    …which seemed to give me the image i wanted, but i could not get the ripple effect to work.

    Other than the change in image source, i just pasted your code into the area where coding usually lives –

    thoughts>?
    much appreciated
    Jess

    • Hi Jess,

      it seems that stuff from your post has gone missing while posting, so i can only guess what your problem is.

      I’ll just assume that you have a same-origin-policy issue here, because this seems to be the most likely:
      The Script (Page) and the Image must be delivered from the same url (i.e. http://www.yourdomain.com) otherwise your browser will not allow the script to manipulate the canvas because of security reasons.
      Please check that both resources are delivered via the same domain AND subdomain, and if you still have problems afterwards please don’t hesitate to write me again.

    • Persistence pays off 🙂
      Add “togglea = false” to the declarations at the top like this:

              var image = document.getElementById(waterImageId),
                  canvas = document.getElementById(waterCanvasId),
                  context = canvas.getContext("2d"),
                  width = canvas.width,
                  height = canvas.height,
                  waterCache1 = [],
                  waterCache2 = [],
                  imageDataSource,
                  imageDataTarget,
                  togglea = false;

      Then add this just before the for loop that does the animation (just before ” for (var x = 2; x < width + 2; x++) " etc):

                  if (togglea) {
                      var blaX = Math.floor(Math.random() * width) + 1;
                      var blaY = Math.floor(Math.random() * height) + 1;
                      waterCache1[blaX][blaY] = 127;
                      waterCache1[blaX + 1][blaY] = 127;
                      waterCache1[blaX - 1][blaY] = 127;
                      waterCache1[blaX][blaY + 1] = 127;
                      waterCache1[blaX][blaY - 1] = 127;
                      togglea = false;
                  } else if (Math.floor(Math.random() * 5) == 1) {
                      togglea = true;
                  }

      Works like a charm 🙂

      And I will definitely be linking here, Timewaster. 🙂 My architect at work pointed this page out to me and the water effect is perfect for a pet project I am working on. Much more direct and cleaner than the others I've found

      • ah, i see you found the script in the source code of this page.

        i only created that so one can see that the image above already has the effect applied to it, hence the really bad code and the “bla” variables and so on, i did not expect people would want to use the droplets…

        i will add it to the official script, but with a bit better code.

  2. Works in IE 11, but very slow effects; Chrome fails completely. Running Windows 10 Pro i7 Core w/8GB and 2GB Vid. I downloaded .zip and unzip to Desktop sub folder /Water/Javascript-Water_Ripples/… I run index.html and Chrome Inspect flags an ERROR: [index.html:58 Uncaught SecurityError: Failed to execute ‘getImageData’ on ‘CanvasRenderingContext2D’: The canvas has been tainted by cross-origin data.window.onload @ index.html:58 ]. I have checked permissions and moved to a C:\Water folder and still have issues? Your demo page works great with a quick response, so I know it should work. I might be something simple like me not enabling it in a WordPress content folder or failing to call a missing CSS. The images show up top and bottom, just does not respond to mouse ripples.

    • hi jason,

      that it is slow in IE11 is no surprise, but nothing i can do there. (there is no performance optimization i can do, it is a browser problem)

      the problem in chrome is exactly what your browser told you: “The canvas has been tainted by cross-origin data”, this means that a resource (the image) that the script tried to copy onto the canvas element did not came from the same domain as the script (website).
      in this case running it locally from your machine is the problem, since both resources (files) did not came from a domain the browser interprets them as foreign to each other.

      this means the script can only run when you put it on a webserver and the image and the script are delivered by the same (sub)domain.

      if you want to run it locally you need to use a browser which is not as strict on the same origin policy.

  3. Hello,

    Thank you for hosting this resource. I am working on an HTML5 game that uses a water rippling effect like this in the background. I am going through your code and trying to understand the logic, but unfortunately, the page that you list as your source for the algorithm for this is no longer around. I imagine it must have been good, because you are not the only person who has referred to it as a source. Would you be willing, publicly or privately, to talk me through what’s going on, mostly in your manipulatePixel function? I haven’t worked on anything down to a pixel-by-pixel level before, and I’m not sure exactly how the numeric values are translating into a meaningful ripple effect.

    Thanks!

Leave a Reply

Your email address will not be published. Required fields are marked *