Edit in JSFiddle

/**
 * jQuery Ripples plugin v0.4.3 / http://github.com/sirxemic/jquery.ripples
 * MIT License
 * @author sirxemic / http://sirxemic.com/
 */

+ function($) {

    var gl;
    var $window = $(window); // There is only one window, so why not cache the jQuery-wrapped window?

    function isPercentage(str) {
        return str[str.length - 1] == '%';
    }

    function hasWebGLSupport() {
        var canvas = document.createElement('canvas');
        var context = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
        var result = context && context.getExtension('OES_texture_float');
        return result;
    }

    var supportsWebGL = hasWebGLSupport();

    function createProgram(vertexSource, fragmentSource, uniformValues) {
        function compileSource(type, source) {
            var shader = gl.createShader(type);
            gl.shaderSource(shader, source);
            gl.compileShader(shader);
            if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
                throw new Error('compile error: ' + gl.getShaderInfoLog(shader));
            }
            return shader;
        }

        var program = {};

        program.id = gl.createProgram();
        gl.attachShader(program.id, compileSource(gl.VERTEX_SHADER, vertexSource));
        gl.attachShader(program.id, compileSource(gl.FRAGMENT_SHADER, fragmentSource));
        gl.linkProgram(program.id);
        if (!gl.getProgramParameter(program.id, gl.LINK_STATUS)) {
            throw new Error('link error: ' + gl.getProgramInfoLog(program.id));
        }

        // Fetch the uniform and attribute locations
        program.uniforms = {};
        program.locations = {};
        gl.useProgram(program.id);
        gl.enableVertexAttribArray(0);
        var name, type, regex = /uniform (\w+) (\w+)/g,
            shaderCode = vertexSource + fragmentSource;
        while ((match = regex.exec(shaderCode)) != null) {
            name = match[2];
            program.locations[name] = gl.getUniformLocation(program.id, name);
        }

        return program;
    }

    function bindTexture(texture, unit) {
        gl.activeTexture(gl.TEXTURE0 + (unit || 0));
        gl.bindTexture(gl.TEXTURE_2D, texture);
    }

    function getBackgroundImageUrl($el) {
        var urlMatch = /url\(["']?([^"']*)["']?\)/.exec($el.css('background-image'));
        if (urlMatch == null) {
            return null;
        }

        return urlMatch[1];
    }

    function isDataUri(url) {
        return url.match(/^data:/);
    }

    // Extend the css
    $('head').prepend('<style>.jquery-ripples { position: relative; z-index: 0; }</style>');

    // RIPPLES CLASS DEFINITION
    // =========================

    var Ripples = function(el, options) {
        var that = this;

        this.$el = $(el);
        this.$el.addClass('jquery-ripples');

        // If this element doesn't have a background image, don't apply this effect to it
        var backgroundUrl = getBackgroundImageUrl(this.$el);
        if (!backgroundUrl) {
            return;
        }

        this.interactive = options.interactive;
        this.resolution = options.resolution || 256;
        this.textureDelta = new Float32Array([1 / this.resolution, 1 / this.resolution]);

        this.perturbance = options.perturbance;
        this.dropRadius = options.dropRadius;

        var canvas = document.createElement('canvas');
        canvas.width = this.$el.innerWidth();
        canvas.height = this.$el.innerHeight();
        this.canvas = canvas;
        this.$canvas = $(canvas);
        this.$canvas.css({
            position: 'absolute',
            left: 0,
            top: 0,
            right: 0,
            bottom: 0,
            zIndex: -1
        });

        this.$el.append(canvas);
        this.context = gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');

        // Load extensions
        gl.getExtension('OES_texture_float');
        var linearSupport = gl.getExtension('OES_texture_float_linear');

        // Init events
        $(window).on('resize', function() {
            var newWidth = that.$el.innerWidth(),
                newHeight = that.$el.innerHeight();

            if (newWidth != that.canvas.width || newHeight != that.canvas.height) {
                canvas.width = newWidth;
                canvas.height = newHeight;
            }
        });

        this.$el.on('mousemove.ripples', function(e) {
            if (that.visible && that.running && that.interactive) {
                that.dropAtMouse(e, that.dropRadius, 0.01);
            }
        }).on('mousedown.ripples', function(e) {
            if (that.visible && that.running && that.interactive) {
                that.dropAtMouse(e, that.dropRadius * 1.5, 0.14);
            }
        });

        this.textures = [];
        this.framebuffers = [];

        for (var i = 0; i < 2; i++) {
            var texture = gl.createTexture();
            var framebuffer = gl.createFramebuffer();

            gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
            framebuffer.width = this.resolution;
            framebuffer.height = this.resolution;

            gl.bindTexture(gl.TEXTURE_2D, texture);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, linearSupport ? gl.LINEAR : gl.NEAREST);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, linearSupport ? gl.LINEAR : gl.NEAREST);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.resolution, this.resolution, 0, gl.RGBA, gl.FLOAT, null);

            gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
            if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
                throw new Error('Rendering to this texture is not supported (incomplete framebuffer)');
            }

            gl.bindTexture(gl.TEXTURE_2D, null);
            gl.bindFramebuffer(gl.FRAMEBUFFER, null);

            this.textures.push(texture);
            this.framebuffers.push(framebuffer);
        }

        this.running = true;

        // Init GL stuff
        this.quad = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, this.quad);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, - 1, + 1, - 1, + 1, + 1, - 1, + 1]), gl.STATIC_DRAW);

        this.initShaders();

        // Init textures
        var image = new Image;

        // Disable CORS when the image source is a data URI.
        image.crossOrigin = isDataUri(backgroundUrl) ? null : options.crossOrigin || '';

        image.onload = function() {
            gl = that.context;

            function isPowerOfTwo(x) {
                return (x & (x - 1)) == 0;
            }

            var wrapping = (isPowerOfTwo(image.width) && isPowerOfTwo(image.height)) ? gl.REPEAT : gl.CLAMP_TO_EDGE;

            that.backgroundWidth = image.width;
            that.backgroundHeight = image.height;

            var texture = gl.createTexture();

            gl.bindTexture(gl.TEXTURE_2D, texture);
            gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapping);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapping);
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);

            that.backgroundTexture = texture;

            // Everything loaded successfully - hide the CSS background image
            that.$el.css('backgroundImage', 'none');
        };
        image.src = backgroundUrl;

        this.visible = true;

        gl.clearColor(0, 0, 0, 0);
        gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

        // Init animation
        function step() {
            that.step();
            requestAnimationFrame(step);
        }

        requestAnimationFrame(step);
    };

    Ripples.DEFAULTS = {
        resolution: 256,
        dropRadius: 20,
        perturbance: 0.03,
        interactive: true,
        crossOrigin: ''
    };

    Ripples.prototype = {

        step: function() {
            gl = this.context;

            if (!this.visible || !this.backgroundTexture) {
                return;
            }

            this.computeTextureBoundaries();

            if (this.running) {
                this.update();
            }

            this.render();
        },

        drawQuad: function() {
            gl.bindBuffer(gl.ARRAY_BUFFER, this.quad);
            gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
            gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
        },

        render: function() {
            gl.viewport(0, 0, this.canvas.width, this.canvas.height);

            gl.enable(gl.BLEND);
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

            gl.useProgram(this.renderProgram.id);

            bindTexture(this.backgroundTexture, 0);
            bindTexture(this.textures[0], 1);

            gl.uniform1f(this.renderProgram.locations.perturbance, this.perturbance);
            gl.uniform2fv(this.renderProgram.locations.topLeft, this.renderProgram.uniforms.topLeft);
            gl.uniform2fv(this.renderProgram.locations.bottomRight, this.renderProgram.uniforms.bottomRight);
            gl.uniform2fv(this.renderProgram.locations.containerRatio, this.renderProgram.uniforms.containerRatio);
            gl.uniform1i(this.renderProgram.locations.samplerBackground, 0);
            gl.uniform1i(this.renderProgram.locations.samplerRipples, 1);

            this.drawQuad();
            gl.disable(gl.BLEND);
        },

        update: function() {
            gl.viewport(0, 0, this.resolution, this.resolution);

            for (var i = 0; i < 2; i++) {
                gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffers[i]);
                bindTexture(this.textures[1 - i]);
                gl.useProgram(this.updateProgram[i].id);

                this.drawQuad();
            }

            gl.bindFramebuffer(gl.FRAMEBUFFER, null);
        },

        computeTextureBoundaries: function() {
            var backgroundSize = this.$el.css('background-size');
            var backgroundAttachment = this.$el.css('background-attachment');
            var backgroundPosition = this.$el.css('background-position').split(' ');

            // Here the 'window' is the element which the background adapts to
            // (either the chrome window or some element, depending on attachment)
            var parElement = backgroundAttachment == 'fixed' ? $window : this.$el;
            var winOffset = parElement.offset() || {
                left: pageXOffset,
                top: pageYOffset
            };
            var winWidth = parElement.innerWidth();
            var winHeight = parElement.innerHeight();

            // TODO: background-clip
            if (backgroundSize == 'cover') {
                var scale = Math.max(winWidth / this.backgroundWidth, winHeight / this.backgroundHeight);

                var backgroundWidth = this.backgroundWidth * scale;
                var backgroundHeight = this.backgroundHeight * scale;
            } else if (backgroundSize == 'contain') {
                var scale = Math.min(winWidth / this.backgroundWidth, winHeight / this.backgroundHeight);

                var backgroundWidth = this.backgroundWidth * scale;
                var backgroundHeight = this.backgroundHeight * scale;
            } else {
                backgroundSize = backgroundSize.split(' ');
                var backgroundWidth = backgroundSize[0] || '';
                var backgroundHeight = backgroundSize[1] || backgroundWidth;

                if (isPercentage(backgroundWidth)) {
                    backgroundWidth = winWidth * parseFloat(backgroundWidth) / 100;
                } else if (backgroundWidth != 'auto') {
                    backgroundWidth = parseFloat(backgroundWidth);
                }

                if (isPercentage(backgroundHeight)) {
                    backgroundHeight = winHeight * parseFloat(backgroundHeight) / 100;
                } else if (backgroundHeight != 'auto') {
                    backgroundHeight = parseFloat(backgroundHeight);
                }

                if (backgroundWidth == 'auto' && backgroundHeight == 'auto') {
                    backgroundWidth = this.backgroundWidth;
                    backgroundHeight = this.backgroundHeight;
                } else {
                    if (backgroundWidth == 'auto') {
                        backgroundWidth = this.backgroundWidth * (backgroundHeight / this.backgroundHeight);
                    }

                    if (backgroundHeight == 'auto') {
                        backgroundHeight = this.backgroundHeight * (backgroundWidth / this.backgroundWidth);
                    }
                }
            }

            // Compute backgroundX and backgroundY in page coordinates
            var backgroundX = backgroundPosition[0] || '';
            var backgroundY = backgroundPosition[1] || backgroundX;

            if (backgroundX == 'left') {
                backgroundX = winOffset.left;
            } else if (backgroundX == 'center') {
                backgroundX = winOffset.left + winWidth / 2 - backgroundWidth / 2;
            } else if (backgroundX == 'right') {
                backgroundX = winOffset.left + winWidth - backgroundWidth;
            } else if (isPercentage(backgroundX)) {
                backgroundX = winOffset.left + (winWidth - backgroundWidth) * parseFloat(backgroundX) / 100;
            } else {
                backgroundX = parseFloat(backgroundX);
            }

            if (backgroundY == 'top') {
                backgroundY = winOffset.top;
            } else if (backgroundY == 'center') {
                backgroundY = winOffset.top + winHeight / 2 - backgroundHeight / 2;
            } else if (backgroundY == 'bottom') {
                backgroundY = winOffset.top + winHeight - backgroundHeight;
            } else if (isPercentage(backgroundY)) {
                backgroundY = winOffset.top + (winHeight - backgroundHeight) * parseFloat(backgroundY) / 100;
            } else {
                backgroundY = parseFloat(backgroundY);
            }

            var elementOffset = this.$el.offset();

            this.renderProgram.uniforms.topLeft = new Float32Array([
            (elementOffset.left - backgroundX) / backgroundWidth, (elementOffset.top - backgroundY) / backgroundHeight]);
            this.renderProgram.uniforms.bottomRight = new Float32Array([
            this.renderProgram.uniforms.topLeft[0] + this.$el.innerWidth() / backgroundWidth,
            this.renderProgram.uniforms.topLeft[1] + this.$el.innerHeight() / backgroundHeight]);

            var maxSide = Math.max(this.canvas.width, this.canvas.height);

            this.renderProgram.uniforms.containerRatio = new Float32Array([
            this.canvas.width / maxSide,
            this.canvas.height / maxSide]);
        },

        initShaders: function() {
            var vertexShader = ['attribute vec2 vertex;', 'varying vec2 coord;', 'void main() {', 'coord = vertex * 0.5 + 0.5;', 'gl_Position = vec4(vertex, 0.0, 1.0);', '}'].join('\n');

            this.dropProgram = createProgram(vertexShader, ['precision highp float;',

            'const float PI = 3.141592653589793;', 'uniform sampler2D texture;', 'uniform vec2 center;', 'uniform float radius;', 'uniform float strength;',

            'varying vec2 coord;',

            'void main() {', 'vec4 info = texture2D(texture, coord);',

            'float drop = max(0.0, 1.0 - length(center * 0.5 + 0.5 - coord) / radius);', 'drop = 0.5 - cos(drop * PI) * 0.5;',

            'info.r += drop * strength;',

            'gl_FragColor = info;', '}'].join('\n'));

            this.updateProgram = [0, 0];
            this.updateProgram[0] = createProgram(vertexShader, ['precision highp float;',

            'uniform sampler2D texture;', 'uniform vec2 delta;',

            'varying vec2 coord;',

            'void main() {', 'vec4 info = texture2D(texture, coord);',

            'vec2 dx = vec2(delta.x, 0.0);', 'vec2 dy = vec2(0.0, delta.y);',

            'float average = (', 'texture2D(texture, coord - dx).r +', 'texture2D(texture, coord - dy).r +', 'texture2D(texture, coord + dx).r +', 'texture2D(texture, coord + dy).r', ') * 0.25;',

            'info.g += (average - info.r) * 2.0;', 'info.g *= 0.995;', 'info.r += info.g;',

            'gl_FragColor = info;', '}'].join('\n'));
            gl.uniform2fv(this.updateProgram[0].locations.delta, this.textureDelta);

            this.updateProgram[1] = createProgram(vertexShader, ['precision highp float;',

            'uniform sampler2D texture;', 'uniform vec2 delta;',

            'varying vec2 coord;',

            'void main() {', 'vec4 info = texture2D(texture, coord);',

            'vec3 dx = vec3(delta.x, texture2D(texture, vec2(coord.x + delta.x, coord.y)).r - info.r, 0.0);', 'vec3 dy = vec3(0.0, texture2D(texture, vec2(coord.x, coord.y + delta.y)).r - info.r, delta.y);', 'info.ba = normalize(cross(dy, dx)).xz;',

            'gl_FragColor = info;', '}'].join('\n'));
            gl.uniform2fv(this.updateProgram[1].locations.delta, this.textureDelta);

            this.renderProgram = createProgram(['precision highp float;',

            'attribute vec2 vertex;', 'uniform vec2 topLeft;', 'uniform vec2 bottomRight;', 'uniform vec2 containerRatio;', 'varying vec2 ripplesCoord;', 'varying vec2 backgroundCoord;', 'void main() {', 'backgroundCoord = mix(topLeft, bottomRight, vertex * 0.5 + 0.5);', 'backgroundCoord.y = 1.0 - backgroundCoord.y;', 'ripplesCoord = vec2(vertex.x, -vertex.y) * containerRatio * 0.5 + 0.5;', 'gl_Position = vec4(vertex.x, -vertex.y, 0.0, 1.0);', '}'].join('\n'), ['precision highp float;',

            'uniform sampler2D samplerBackground;', 'uniform sampler2D samplerRipples;', 'uniform float perturbance;', 'varying vec2 ripplesCoord;', 'varying vec2 backgroundCoord;',

            'void main() {', 'vec2 offset = -texture2D(samplerRipples, ripplesCoord).ba;', 'float specular = pow(max(0.0, dot(offset, normalize(vec2(-0.6, 1.0)))), 4.0);', 'gl_FragColor = texture2D(samplerBackground, backgroundCoord + offset * perturbance) + specular;', '}'].join('\n'));
        },

        dropAtMouse: function(e, radius, strength) {
            var borderLeft = parseInt(this.$el.css('border-left-width')) || 0,
                borderTop = parseInt(this.$el.css('border-top-width')) || 0;

            this.drop(
            e.pageX - this.$el.offset().left - borderLeft,
            e.pageY - this.$el.offset().top - borderTop,
            radius,
            strength);
        },

        drop: function(x, y, radius, strength) {
            var that = this;

            gl = this.context;

            var elWidth = this.$el.innerWidth();
            var elHeight = this.$el.innerHeight();
            var longestSide = Math.max(elWidth, elHeight);

            radius = radius / longestSide;

            var dropPosition = new Float32Array([
            (2 * x - elWidth) / longestSide, (elHeight - 2 * y) / longestSide]);

            gl.viewport(0, 0, this.resolution, this.resolution);

            // Render onto texture/framebuffer 0
            gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffers[0]);

            // Using texture 1
            bindTexture(this.textures[1]);

            gl.useProgram(this.dropProgram.id);
            gl.uniform2fv(this.dropProgram.locations.center, dropPosition);
            gl.uniform1f(this.dropProgram.locations.radius, radius);
            gl.uniform1f(this.dropProgram.locations.strength, strength);

            this.drawQuad();

            // Switch textures
            var t = this.framebuffers[0];
            this.framebuffers[0] = this.framebuffers[1];
            this.framebuffers[1] = t;
            t = this.textures[0];
            this.textures[0] = this.textures[1];
            this.textures[1] = t;

            gl.bindFramebuffer(gl.FRAMEBUFFER, null);
        },

        // Actions
        destroy: function() {
            this.canvas.remove();
            this.$el.off('.ripples');
            this.$el.css('backgroundImage', '');
            this.$el.removeClass('jquery-ripples').removeData('ripples');
        },

        show: function() {
            this.$canvas.show();
            this.$el.css('backgroundImage', 'none');
            this.visible = true;
        },

        hide: function() {
            this.$canvas.hide();
            this.$el.css('backgroundImage', '');
            this.visible = false;
        },

        pause: function() {
            this.running = false;
        },

        play: function() {
            this.running = true;
        },

        set: function(property, value) {
            switch (property) {
                case 'dropRadius':
                case 'perturbance':
                case 'interactive':
                    this[property] = value;
                    break;
            }
        }
    };

    // RIPPLES PLUGIN DEFINITION
    // ==========================

    var old = $.fn.ripples;

    $.fn.ripples = function(option) {
        if (!supportsWebGL) {
            throw new Error('Your browser does not support WebGL or the OES_texture_float extension.');
        }

        var args = (arguments.length > 1) ? Array.prototype.slice.call(arguments, 1) : undefined;

        return this.each(function() {
            var $this = $(this),
                data = $this.data('ripples'),
                options = $.extend({}, Ripples.DEFAULTS, $this.data(), typeof option == 'object' && option);

            if (!data && typeof option == 'string') {
                return;
            }
            if (!data) {
                $this.data('ripples', (data = new Ripples(this, options)));
            } else if (typeof option == 'string') {
                Ripples.prototype[option].apply(data, args);
            }
        });
    };

    $.fn.ripples.Constructor = Ripples;


    // RIPPLES NO CONFLICT
    // ====================

    $.fn.ripples.noConflict = function() {
        $.fn.ripples = old;
        return this;
    };

}(window.jQuery); 



$(document).ready(function() {
	try {
		$('body').ripples({
			resolution: 128,
			dropRadius: 10, //px
			perturbance: 0.04,
		});
	
	}
	catch (e) {
		$('.error').show().text(e);
	}

	// Automatic drops
	setInterval(function() {
		var $el = $('main');
		var x = Math.random() * $el.outerWidth();
		var y = Math.random() * $el.outerHeight();
		var dropRadius = 20;
		var strength = 0.04 + Math.random() * 0.04;
		$el.ripples('drop', x, y, dropRadius, strength);
	}, 400);
});
* { margin: 0; padding: 0; }
html {
	height: 100%;
}
body {
	color: #fff;
	font-size: 16px;
	background-image: url(https://unsplash.it/1000/800/?random);
	background-size: cover;
	background-position: 50% 0;
	height: 100%;
	text-align: center;
}
body:before {
	content: '';
	display: inline-block;
	vertical-align: middle;
	height: 100%;
}
b {
  padding:15px;
width:100%;
background:rgba(0,0,0,0.6);
}
<b>結構重いですね。表示に時間かかります</b>