Very new to WebGL and attempting to port some 2D image processing shaders in order to get a handle on things. I initially was misled by the MDN tutorials into thinking WebGL was like OpenGL desktop, but then found these tutorials, which I found much more true to form as well as my purposes. However, I'm still having some trouble in formatting a render loop so I can pass a continually updating texture for processing. In cases where it does render, I just get a muddy mess and in cases where the vertex shader isn't a simple pass through I get nothing. I understand GLSL and the basics of how buffers work, but clearly am doing something very wrong here... Any help would be greatly appreciated. Thanks!
class GL {
    constructor(canvas){
        this.gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
        if (!this.gl) {
            alert("Unable to initialize WebGL. Your browser may not support it.");
            this.gl = null;
        }
 
        //init shaders
        var fragmentShader = getShader(this.gl, "fshader");
        var vertexShader = getShader(this.gl, "vshader");
        var shaderProgram = this.gl.createProgram();
        this.gl.attachShader(shaderProgram, vertexShader);
        this.gl.attachShader(shaderProgram, fragmentShader);
        this.gl.linkProgram(shaderProgram);
        this.gl.useProgram(shaderProgram);
        
        this.positionLocation = this.gl.getAttribLocation(shaderProgram, "position");
        this.texCoordLocation = this.gl.getAttribLocation(shaderProgram, "texcoord");
        var resolutionLocation = this.gl.getUniformLocation(shaderProgram, "resolution");
        this.width = this.gl.getUniformLocation(shaderProgram, "width");
    
        //set resolution
        this.gl.uniform2f(resolutionLocation, canvas.width, canvas.height);
        
        function getShader(gl, id) {
            var shaderScript, theSource, currentChild, shader;
            shaderScript = document.getElementById(id);
            if (!shaderScript) {
                return null;
            }
            theSource = "";
            currentChild = shaderScript.firstChild;
            
            while(currentChild) {
                if (currentChild.nodeType == currentChild.TEXT_NODE) {
                    theSource += currentChild.textContent;
                }
                currentChild = currentChild.nextSibling;
            }
            if (shaderScript.type == "x-shader/x-fragment") {
                shader = gl.createShader(gl.FRAGMENT_SHADER);
            } else if (shaderScript.type == "x-shader/x-vertex") {
                shader = gl.createShader(gl.VERTEX_SHADER);
            } else {
                // Unknown shader type
                return null;
            }
            gl.shaderSource(shader, theSource);
            gl.compileShader(shader);
            if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
                alert("An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader));
                return null;
            }
                return shader;
        };
    };
    
    render(bufferCanvas, x, y) { 
        this.gl.clear(this.gl.COLOR_BUFFER_BIT);
        //texture coordinates
        var texCoordBuffer = this.gl.createBuffer();
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.texCoordBuffer);
        this.gl.enableVertexAttribArray(this.texCoordLocation);
        this.gl.vertexAttribPointer(this.texCoordLocation, 2, this.gl.FLOAT, false, 8, 0);
        
        this.gl.bufferData(
            this.gl.ARRAY_BUFFER, 
            new Float32Array([
                0.0,  0.0,
                1.0,  0.0,
                0.0,  1.0,
                0.0,  1.0,
                1.0,  0.0,
                1.0,  1.0]), 
            this.gl.STATIC_DRAW);
        
        //create texture
        var texture = this.gl.createTexture();
        this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
        
        //normalize image to powers of two
        this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
        this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
        this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST);
        this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST);
        
        //load texture from 2d canvas
        this.gl.texImage2D(this.gl.TEXTURE_2D, 
                           0, 
                           this.gl.RGBA, 
                           this.gl.RGBA, 
                           this.gl.UNSIGNED_BYTE, 
                           bufferCanvas);
        //load buffer
        var buffer = this.gl.createBuffer();
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
        this.gl.enableVertexAttribArray(this.positionLocation);
        this.gl.vertexAttribPointer(this.positionLocation, 2, this.gl.FLOAT, false, 12, 0);
 
        //draw size and position
        this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array([   
            x, y,
            x + bufferCanvas.width, y,
            x, y + bufferCanvas.height,
            x, y + bufferCanvas.height,
            x+ bufferCanvas.width, y,
            x+ bufferCanvas.width, y + bufferCanvas.height]), this.gl.STATIC_DRAW);
        
        //blur width
        this.gl.enableVertexAttribArray(this.width);
        this.gl.vertexAttribPointer(this.width, 1, this.gl.FLOAT, false, 12, 8);
        
        //draw
        this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);
    };
};
var canvas2d = document.getElementById('buffer-canvas');
var context2d = canvas2d.getContext("2d");
var canvasGL = new GL(document.getElementById('main-canvas'));
canvasGL.width = 5.0;
for(var i=0; i<10; i++) {
    var r = Math.floor(Math.random() * 255);
    var g = Math.floor(Math.random() * 255);
    var b = Math.floor(Math.random() * 255);
    var a = Math.floor(Math.random() * 255);
    context2d.fillStyle = "rgba(" + r + "," + g + "," + b + "," + a + ")";
    
    var x = Math.random() * canvas2d.width;
    var y = Math.random() * canvas2d.height;
    var width = canvas2d.width - (Math.random() * canvas2d.width);
    var height = canvas2d.height - (Math.random() * canvas2d.height);
    context2d.fillRect(x, y, width , height);
    
    canvasGL.render(canvas2d, canvas2d.getBoundingClientRect("left"), canvas2d.getBoundingClientRect("top"));
}<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sylvester/0.1.3/sylvester.min.js"></script>
<script src="https://github.com/mdn/webgl-examples/blob/gh-pages/tutorial/glUtils.js"></script>
  
<script id="vshader" type="x-shader/x-vertex">
precision mediump float;
attribute vec2 position;
attribute vec2 texcoord;
uniform vec2 resolution;
uniform float width;
varying vec2 texcoord11;
varying vec2 texcoord00;
varying vec2 texcoord02;
varying vec2 texcoord20;
varying vec2 texcoord22;
void main()
{
    gl_Position = vec4(((position / resolution) * 2.0 - 1.0) * vec2(1, -1), 0, 1);
 // get texcoords
 texcoord11 = texcoord;
 texcoord00 = texcoord + vec2(-width, -width);
 texcoord02 = texcoord + vec2( width, -width);
 texcoord20 = texcoord + vec2( width,  width);
 texcoord22 = texcoord + vec2(-width,  width);
}
</script>
  
<script id="fshader" type="x-shader/x-fragment">
precision mediump float;
uniform sampler2D image;
varying vec2 texcoord11;
varying vec2 texcoord00;
varying vec2 texcoord02;
varying vec2 texcoord20;
varying vec2 texcoord22;
void main()
{
 vec4 blur;
 
 blur = texture2D(image, texcoord11);
 blur += texture2D(image, texcoord00);
 blur += texture2D(image, texcoord02);
 blur += texture2D(image, texcoord20);
 blur += texture2D(image, texcoord22);
 gl_FragColor = 0.2 * blur;
}
</script>
</head>
<body>
<canvas id="main-canvas" width="400" height="300" style="border:1px solid black;"></canvas>
<canvas id="buffer-canvas" width="400" height="300" style="visibility:hidden;"></canvas>
</body> 
     
    