body {
  margin: 0;
}

canvas {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}
import {
  Renderer,
  Program,
  Texture,
  Mesh,
  Vec2,
  Flowmap,
  Triangle,
  TextureLoader
} from "https://cdn.skypack.dev/ogl@0.0.93";

const vertex = /* glsl */ `
attribute vec2 uv;
attribute vec2 position;
varying vec2 vUv;

void main() {
  vUv = uv;
  gl_Position = vec4(position, 0, 1);
}
`;

const fragment = /* glsl */ `
precision highp float;
uniform sampler2D tImage;
uniform sampler2D tFlow;
uniform float uTime;
varying vec2 vUv;

void main() {
  // R and G values are velocity in the x and y direction
  // B value is the velocity length
  vec3 flow = texture2D(tFlow, vUv).rgb;
  
  // Use flow to adjust the uv lookup of a texture
  vec2 uv = vUv;
  uv +=  (flow.rg * flow.b * 50.0);
  // uv +=  (flow.rg * 5.0);
  vec3 tex = texture2D(tImage, uv).rgb;
  
  gl_FragColor.rgb = tex;
  // gl_FragColor.rgb = flow;
  gl_FragColor.a = 1.0;
}
`;

const renderer = new Renderer();
const gl = renderer.gl;
document.body.appendChild(gl.canvas);

const mouse = new Vec2(0.5);
const lastMouse = new Vec2(0.5);
const velocity = new Vec2();
let aspect = 1;

function resize() {
  aspect = renderer.width / renderer.height;
  renderer.setSize(window.innerWidth, window.innerHeight);
}

window.addEventListener("resize", resize, false);
resize();

const flowmap = new Flowmap(gl, { falloff: 0.4, dissipation: 0.99, size: 512 });
const geometry = new Triangle(gl);

const texture = TextureLoader.load(gl, {
  wrapS: gl.MIRRORED_REPEAT,
  wrapT: gl.MIRRORED_REPEAT,
  src: {
    jpg:
      "https://images.unsplash.com/photo-1572040917409-60ca0cfcc2df?crop=entropy&cs=tinysrgb&fm=jpg&ixlib=rb-1.2.1&q=80&raw_url=true&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1024"
  }
});

const program = new Program(gl, {
  vertex,
  fragment,
  uniforms: {
    uTime: { value: 0 },
    tImage: { value: texture },
    tFlow: flowmap.uniform
  }
});

const mesh = new Mesh(gl, { geometry, program });

function updateMouse({ clientX, clientY }) {
  const rect = renderer.gl.canvas.getBoundingClientRect();
  const aspect = renderer.width / renderer.height;
  const x = (clientX - rect.x) / renderer.width;
  const y = 1 - (clientY - rect.y) / renderer.height;
  mouse.set(x, y);
}

const isTouchCapable = "ontouchstart" in window;
if (isTouchCapable) {
  window.addEventListener("touchstart", updateMouse, false);
  window.addEventListener("touchmove", updateMouse, false);
} else {
  window.addEventListener("mousemove", updateMouse, false);
}

const spring = 0.02;
const friction = 0.85;
const springVel = new Vec2();

function update(t) {
  springVel.copy(mouse).sub(lastMouse).multiply(spring);
  velocity.add(springVel).multiply(friction);
  lastMouse.add(velocity);

  flowmap.mouse.copy(lastMouse);
  flowmap.velocity.copy(velocity);
  flowmap.aspect = aspect;

  flowmap.update();

  renderer.render({ scene: mesh });
  requestAnimationFrame(update);
}

requestAnimationFrame(update);

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.