Oscillators and Bias
You can solve this by using generators in combination with a grid. Each line in the grid fluctuates, and the difference between the lines is used to offset the image segment.
The simplest approach, in my opinion, is to create the first object of the oscillator. It can be very simple, but the bottom line is that an object can be created and it tracks the current values locally.
Step 1: Oscillator Object
function Osc(speed) { var frame = 0;
For example, expand an object to use frequency, amplitude, and speed as parameters. If many of them are used, also consider a prototype approach.
If we create a simple grid of five positions, in which the three middle vertical lines oscillate (edges that make up lines 1 and 5.), we get the following result (no change):

Animated visualization of oscillating grid lines:
function Osc(speed) { var frame = 0; this.current = function(x) { frame += 0.005 * speed; return Math.sin(frame + x * speed); }; } var canvas = document.querySelector("canvas"), ctx = canvas.getContext("2d"), w = canvas.width, h = canvas.height; var o1 = new Osc(0.05),
<canvas width=230 height=250></canvas>
Step 2: use the difference between the lines in the grid for the image
The next step is to simply calculate the difference between the created points for each row.

The math is straightforward, we only need to make sure that the lines do not overlap, as this will create a negative width:
// initial static values representing the grid line positions: var w = canvas.width, h = canvas.height, x0 = 0, x1 = w * 0.25, x2 = w * 0.5, x3 = w * 0.75, x4 = w; // absolute positions for wavy lines var lx1 = x1 + o1.current(y*0.2); // 0.2 is arbitrary and tweak-able var lx2 = x2 + o2.current(y*0.2); var lx3 = x3 + o3.current(y*0.2); // calculate each segment width var w0 = lx1; // - x0 var w1 = lx2 - lx1; var w2 = lx3 - lx2; var w3 = x4 - lx3;
If we send these values to drawImage() for the destination using static fixed widths (i.e. mesh cell size) for the source, we get the result as shown below.
We do not need to drawImage() over the pixels in the bitmap, since drawImage() can be hardware accelerated, we do not need to fulfill the CORS requirements and do the interpolation for us:
var img = new Image(); img.onload = waves; img.src = "https://i.imgur.com/PwzfNTk.png"; function waves() { var canvas = document.querySelector("canvas"), ctx = canvas.getContext("2d"), w = canvas.width, h = canvas.height; ctx.drawImage(this, 0, 0); var o1 = new Osc(0.05), o2 = new Osc(0.03), o3 = new Osc(0.06), x0 = 0, x1 = w * 0.25, x2 = w * 0.5, x3 = w * 0.75, x4 = w; (function loop() { ctx.clearRect(0, 0, w, h); for (var y = 0; y < h; y++) {
<canvas width=230 height=300></canvas>
Please note: since we use fractional values, we need to compensate for half the pixel in order to overlap the previous segment, since the final pixel can be interpolated. Otherwise, we get visible wavy lines as a result. We could use integer values, but this will create a more “uneven” animation.
Of course, the values of the oscillators must be changed, the grid size, etc.
The next step is to repeat the oscillators for the horizontal axis and use the canvas itself as the image source.
Optimization and performance
Using the canvas itself as a caveat source
When you draw something from the canvas onto yourself, the browser should, in accordance with the specifications , make a copy of the current content, use this as a source for the destination area.
When a canvas or CanvasRenderingContext2D object is drawn on itself, the drawing model requires the source to be copied before the image, so you can copy parts of the canvas or bitmap with scratches to overlapping parts of yourself.
This means that for each drawImage() operation, where we use the canvas itself as a source, this copying process will be performed.
This can affect performance, so to avoid this, we can use the second canvas element, onto which we first copy the finished vertical pass, and then use the second canvas as the source for the horizontal pass.
LUT and value caching
To increase performance, cache all calculations of values that can be cached. For example, the source width is higher for each segment (x1-x0, etc.). It can be cached by the sw variable (or some other name). This is the so-called micro-optimization, but this is the case when it can make a difference.
For sine, scale, etc. It may be an advantage to cache calculations in the LUT or lookup table. Frequencies can be selected so that the length of the table matches at some level. I do not show it here, but something to think about whether the browser should keep up if the grid has a high resolution.
Integer values
Using integer values and disabling image smoothing is also an option. The result is not as good as with fractional values, but it will give retro-ish to watch the animation and work better.
Sprite sheets
Dynamic pre-generation of frames as a sprite as a last resort is possible. This is more than hunger and has an initial cost, but will work smoothly in almost any situation. The challenge is to find the loop point and not use too much memory.
Alpha Images
Avoiding images with an alpha channel (as in the demo version below) will help, since you will need to clear two times longer, one for the screen canvas, one for the main canvas. Otherwise, the previous move will be displayed in the background.
DEMO OF FINAL RESULT
Here is a complete demo with vertical and horizontal wavy lines. For simplicity, I use only the 4x4 series.
The result does not look completely identical to the examples, but should give an idea. It is simply a matter of increasing the resolution of the grid and adjusting the parameters. In addition, the examples provided in the question are pre-animated with the addition of effects and layers that cannot be achieved only with waves / offsets.
Other changes are that now the overlap of each segment extends throughout the segment, simply adding 0.5 at the beginning, but also at the end. Horizontal pass also refers to the difference in width in a row.
Click "Full Page" when you launch the demo version below to better see the details.
var img = new Image(); img.onload = waves; img.src = "https://i.imgur.com/nMZoUok.png"; function waves() { var canvas = document.querySelector("canvas"), ctx = canvas.getContext("2d"), w = canvas.width, h = canvas.height; ctx.drawImage(this, 0, 0); var o1 = new Osc(0.05), o2 = new Osc(0.03), o3 = new Osc(0.06),
html, body {width:100%;height:100%;background:#555;margin:0;overflow:hidden} canvas {background:url(http://i.imgur.com/KbKlmpk.png);background-size:cover;height:100%;width:auto;min-height:300px}
<canvas width=230 height=300></canvas>