<!DOCTYPE html>
<html lang="en-us">
<head>
  <meta charset="utf-8">
  <meta name="robots" content="noindex">
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
  <title>Basic Mouse/Touch Interactions</title>
  <style>
    html,
    body,
    canvas {
      margin: 0;
      padding: 0;
      width: 100%;
      height: 100%;
      overflow: hidden;
    }
  </style>
</head>
<body>
  <canvas id="canvas"></canvas>
<!--
  © Adam Murray 2024
  https://adammurray.link/

  Creative Commons License
  Attribution-NonCommercial-ShareAlike 4.0 International
  https://creativecommons.org/licenses/by-nc-sa/4.0/
-->    
  
  <script id="vertexShader" type="x-shader/x-vertex">
    #version 300 es
    in vec4 vertexPosition;
    void main() { // no-op vertex shader
      gl_Position = vertexPosition;
    }
  </script>
  
  
  <script id="fragmentShader" type="x-shader/x-fragment">
    #version 300 es
    precision highp float;
  
    uniform vec2 canvasSize;
    uniform vec2 pointerPosition;
    out vec4 fragColor;
  
    void main() {
      vec2 coord = gl_FragCoord.xy - canvasSize/2.;
      float minDim = min(canvasSize.x, canvasSize.y);
      float outsideOfCircle = step(minDim/10., length(coord - pointerPosition));
      // green outside circle, blue inside:
      fragColor = vec4(0, outsideOfCircle, 1.-outsideOfCircle, 1);
    }
  </script>
  
  <script type="text/javascript">
    const gl = canvas.getContext("webgl2");
    if (!gl) {
      main.innerHTML =
        '<p>Error: WebGL2 is <a href="https://get.webgl.org/webgl2/">not supported by your browser</a></p>';
      throw "WebGL2 not supported";
    }
  
    function createShader(shaderType, sourceCode) {
      const shader = gl.createShader(shaderType);
      gl.shaderSource(shader, sourceCode);
      gl.compileShader(shader);
      if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) throw gl.getShaderInfoLog(shader);
      return shader;
    }
  
    const program = gl.createProgram();
    gl.attachShader(program, createShader(gl.VERTEX_SHADER, vertexShader.textContent.trim()));
    gl.attachShader(program, createShader(gl.FRAGMENT_SHADER, fragmentShader.textContent.trim()));
    gl.linkProgram(program);
    if (!gl.getProgramParameter(program, gl.LINK_STATUS)) throw gl.getProgramInfoLog(program);
    gl.useProgram(program);
  
    const vertices = [
      [-1, -1],
      [1, -1],
      [-1, 1],
      [1, 1],
    ];
    const vertexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices.flat()), gl.STATIC_DRAW);
    const vertexPosition = gl.getAttribLocation(program, "vertexPosition");
    gl.enableVertexAttribArray(vertexPosition);
    gl.vertexAttribPointer(vertexPosition, 2, gl.FLOAT, false, 0, 0);
  
    const pointerPositionUniform = gl.getUniformLocation(program, "pointerPosition");
    const canvasSizeUniform = gl.getUniformLocation(program, "canvasSize");
  
    function draw(pointerEvent) {
      const width = canvas.clientWidth;
      const height = canvas.clientHeight;
      canvas.width = width;
      canvas.height = height;
      gl.viewport(0, 0, width, height);
      gl.uniform2f(canvasSizeUniform, width, height);
  
      if (pointerEvent) {
        const canvasRect = canvas.getBoundingClientRect();
        // offset by canvas.width/2 and canvas.height/2 like we do with gl_FragCoord in the shader:
        const x = pointerEvent.clientX - canvasRect.left - canvasRect.width / 2;
        const y = -(pointerEvent.clientY - canvasRect.top - canvasRect.height / 2);
        gl.uniform2f(pointerPositionUniform, x, y);
      } else {
        gl.uniform2f(pointerPositionUniform, 0, 0);
      }
      gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertices.length);
    }
    draw();
  
    // Only start dragging from the canvas...
    canvas.addEventListener("pointerdown", (event) => {
      this.pointerdown = true;
      draw(event);
    });
    // but allow dragging around the entire window:
    window.addEventListener("pointermove", (event) => {
      if (this.pointerdown) draw(event);
    });
    window.addEventListener("pointerup", () => (this.pointerdown = false));
  
    // And don't scroll when sliding around the canvas on mobile:
    canvas.addEventListener("touchmove", (event) => event.preventDefault());
  
    window.addEventListener("resize", () => draw());
  </script>
  
</body>
</html>