diff --git a/index.html b/index.html
index 1f27f96860b589ae0f0304f870516b3bdf08d408..1cf7070b4f9b000d014a765524f242758c828e25 100644
--- a/index.html
+++ b/index.html
@@ -91,17 +91,41 @@
     <br/><hr>
 	<label>lookAt | </label>
     <br/>
-    <label>eye vector : </label>
-    <input style="width:400px" id="x_move" type="range" min="-100" max="100" value="0" oninput="fn_update_xmove(this.value);"></input>
-	<input style="width:60px" type="text" id="textXMove" value="0">
+    <label>eye vector (x-axis) : </label>
+    <input style="width:400px" id="x_eye" type="range" min="-100" max="100" value="0" oninput="fn_update_xeye(this.value);"></input>
+	<input style="width:60px" type="text" id="textXEye" value="0">
     <br/>
-    <label>Y-axis  : </label>
-    <input style="width:400px" id="y_move" type="range" min="-100" max="100" value="0" oninput="fn_update_ymove(this.value);"></input>
-	<input style="width:60px" type="text" id="textYMove" value="0">
+    <label>eye vector (y-axis) : </label>
+    <input style="width:400px" id="y_eye" type="range" min="-100" max="100" value="0" oninput="fn_update_yeye(this.value);"></input>
+	<input style="width:60px" type="text" id="textYEye" value="0">
     <br/>
-    <label>Z-axis Move : </label>
-    <input style="width:400px" id="z_move" type="range" min="-100" max="100" value="0" oninput="fn_update_zmove(this.value);"></input>
-	<input style="width:60px" type="text" id="textZMove" value="0">
+    <label>eye vector (z-axis) : </label>
+    <input style="width:400px" id="z_eye" type="range" min="-100" max="100" value="0" oninput="fn_update_zeye(this.value);"></input>
+	<input style="width:60px" type="text" id="textZEye" value="2">
+    <br/><hr>
+    <label>center vector (x-axis) : </label>
+    <input style="width:400px" id="x_center" type="range" min="-100" max="100" value="0" oninput="fn_update_xcenter(this.value);"></input>
+	<input style="width:60px" type="text" id="textXCenter" value="0">
+    <br/>
+    <label>center vector (y-axis) : </label>
+    <input style="width:400px" id="y_center" type="range" min="-100" max="100" value="0" oninput="fn_update_ycenter(this.value);"></input>
+	<input style="width:60px" type="text" id="textYCenter" value="0">
+    <br/>
+    <label>center vector (z-axis) : </label>
+    <input style="width:400px" id="z_center" type="range" min="-100" max="100" value="0" oninput="fn_update_zcenter(this.value);"></input>
+	<input style="width:60px" type="text" id="textZCenter" value="0">
+    <br/><hr>
+    <label>up vector (x-axis) : </label>
+    <input style="width:400px" id="x_up" type="range" min="-100" max="100" value="0" oninput="fn_update_xup(this.value);"></input>
+	<input style="width:60px" type="text" id="textXUp" value="0">
+    <br/>
+    <label>up vector (y-axis) : </label>
+    <input style="width:400px" id="y_up" type="range" min="-100" max="100" value="0" oninput="fn_update_yup(this.value);"></input>
+	<input style="width:60px" type="text" id="textYUp" value="1">
+    <br/>
+    <label>up vector (z-axis) : </label>
+    <input style="width:400px" id="z_up" type="range" min="-100" max="100" value="0" oninput="fn_update_zup(this.value);"></input>
+	<input style="width:60px" type="text" id="textZUp" value="0">
     <br/><hr>
 	</table>
 	<br/><br/>
diff --git a/script.js b/script.js
index 7b95e6fd59538536139d7be1356a3f4d0fc55e03..17b54e53c68acad480ada2e47968e94f5dd0ab9a 100644
--- a/script.js
+++ b/script.js
@@ -176,7 +176,17 @@ var xMove = 0.0;
 var yMove = 0.0;
 var zMove = 0.0;
 
-var rotate_axis = 0.0;
+var xEye = 0.0;
+var yEye = 0.0;
+var zEye = 2.0;
+
+var xCenter = 0.0;
+var yCenter = 0.0;
+var zCenter = 0.0;
+
+var xUp = 0.0;
+var yUp = 1.0;
+var zUp = 0.0;
 /* modify end */
 
 function fn_speed_scale(a)
@@ -267,6 +277,71 @@ function fn_update_zmove(val)
   zMove = val; 
 }
 
+
+function fn_update_xeye(val)
+{
+  val = val / 100.0;
+  document.getElementById('textXEye').value = val; 
+  xEye = val; 
+}
+
+function fn_update_yeye(val)
+{
+  val = val / 100.0;
+  document.getElementById('textYEye').value = val; 
+  yEye = val; 
+}
+
+function fn_update_zeye(val)
+{
+  val = val / 100.0;
+  document.getElementById('textZEye').value = val; 
+  zEye = val; 
+}
+
+
+function fn_update_xcenter(val)
+{
+  val = val / 100.0;
+  document.getElementById('textXCenter').value = val; 
+  xCenter = val; 
+}
+
+function fn_update_ycenter(val)
+{
+  val = val / 100.0;
+  document.getElementById('textYCenter').value = val; 
+  yCenter = val; 
+}
+
+function fn_update_zcenter(val)
+{
+  val = val / 100.0;
+  document.getElementById('textZCenter').value = val; 
+  zCenter = val; 
+}
+
+function fn_update_xup(val)
+{
+  val = val / 100.0;
+  document.getElementById('textXUp').value = val; 
+  xUp = val; 
+}
+
+function fn_update_yup(val)
+{
+  val = val / 100.0;
+  document.getElementById('textYUp').value = val; 
+  yUp = val; 
+}
+
+function fn_update_zup(val)
+{
+  val = val / 100.0;
+  document.getElementById('textZUp').value = val; 
+  zUp = val; 
+}
+
 /* modify end */
 
 function fn_toggle(mode)
@@ -322,7 +397,7 @@ function renderScene() {
   mat4.rotateZ(mMat, mMat, zRot);
   
 	mat4.perspective(pMat, fov_degree * 3.141592 / 180.0 , 8.0/6.0 , 0.5, 6); 
-	mat4.lookAt(vMat, [0,0,2], [0.0 ,0.0, 0.0], [0,1,0]);
+	mat4.lookAt(vMat, [xEye,yEye,zEye], [xCenter, yCenter, zCenter], [xUp, yUp, zUp]);
   // mat4.frustum(vMat, -8.0/6.0, 8.0/6.0, 1, 1, 1, ); 
   
 	if (flag_animation == 1)
diff --git a/temp.js b/temp.js
deleted file mode 100644
index e3450dc8ee53a5085bdcd69ece2e677666fb246a..0000000000000000000000000000000000000000
--- a/temp.js
+++ /dev/null
@@ -1,1373 +0,0 @@
-/*
- * Copyright 2021 GFXFundamentals.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of GFXFundamentals. nor the names of his
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-(function(root, factory) {  // eslint-disable-line
-    if (typeof define === 'function' && define.amd) {
-      // AMD. Register as an anonymous module.
-      define([], function() {
-        return factory.call(root);
-      });
-    } else {
-      // Browser globals
-      root.webglUtils = factory.call(root);
-    }
-  }(this, function() {
-    'use strict';
-  
-    const topWindow = this;
-  
-    /** @module webgl-utils */
-  
-    function isInIFrame(w) {
-      w = w || topWindow;
-      return w !== w.top;
-    }
-  
-    if (!isInIFrame()) {
-      console.log("%c%s", 'color:blue;font-weight:bold;', 'for more about webgl-utils.js see:');  // eslint-disable-line
-      console.log("%c%s", 'color:blue;font-weight:bold;', 'https://webglfundamentals.org/webgl/lessons/webgl-boilerplate.html');  // eslint-disable-line
-    }
-  
-    /**
-     * Wrapped logging function.
-     * @param {string} msg The message to log.
-     */
-    function error(msg) {
-      if (topWindow.console) {
-        if (topWindow.console.error) {
-          topWindow.console.error(msg);
-        } else if (topWindow.console.log) {
-          topWindow.console.log(msg);
-        }
-      }
-    }
-  
-  
-    /**
-     * Error Callback
-     * @callback ErrorCallback
-     * @param {string} msg error message.
-     * @memberOf module:webgl-utils
-     */
-  
-  
-    /**
-     * Loads a shader.
-     * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
-     * @param {string} shaderSource The shader source.
-     * @param {number} shaderType The type of shader.
-     * @param {module:webgl-utils.ErrorCallback} opt_errorCallback callback for errors.
-     * @return {WebGLShader} The created shader.
-     */
-    function loadShader(gl, shaderSource, shaderType, opt_errorCallback) {
-      const errFn = opt_errorCallback || error;
-      // Create the shader object
-      const shader = gl.createShader(shaderType);
-  
-      // Load the shader source
-      gl.shaderSource(shader, shaderSource);
-  
-      // Compile the shader
-      gl.compileShader(shader);
-  
-      // Check the compile status
-      const compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
-      if (!compiled) {
-        // Something went wrong during compilation; get the error
-        const lastError = gl.getShaderInfoLog(shader);
-        errFn('*** Error compiling shader \'' + shader + '\':' + lastError + `\n` + shaderSource.split('\n').map((l,i) => `${i + 1}: ${l}`).join('\n'));
-        gl.deleteShader(shader);
-        return null;
-      }
-  
-      return shader;
-    }
-  
-    /**
-     * Creates a program, attaches shaders, binds attrib locations, links the
-     * program and calls useProgram.
-     * @param {WebGLShader[]} shaders The shaders to attach
-     * @param {string[]} [opt_attribs] An array of attribs names. Locations will be assigned by index if not passed in
-     * @param {number[]} [opt_locations] The locations for the. A parallel array to opt_attribs letting you assign locations.
-     * @param {module:webgl-utils.ErrorCallback} opt_errorCallback callback for errors. By default it just prints an error to the console
-     *        on error. If you want something else pass an callback. It's passed an error message.
-     * @memberOf module:webgl-utils
-     */
-    function createProgram(
-        gl, shaders, opt_attribs, opt_locations, opt_errorCallback) {
-      const errFn = opt_errorCallback || error;
-      const program = gl.createProgram();
-      shaders.forEach(function(shader) {
-        gl.attachShader(program, shader);
-      });
-      if (opt_attribs) {
-        opt_attribs.forEach(function(attrib, ndx) {
-          gl.bindAttribLocation(
-              program,
-              opt_locations ? opt_locations[ndx] : ndx,
-              attrib);
-        });
-      }
-      gl.linkProgram(program);
-  
-      // Check the link status
-      const linked = gl.getProgramParameter(program, gl.LINK_STATUS);
-      if (!linked) {
-          // something went wrong with the link
-          const lastError = gl.getProgramInfoLog(program);
-          errFn('Error in program linking:' + lastError);
-  
-          gl.deleteProgram(program);
-          return null;
-      }
-      return program;
-    }
-  
-    /**
-     * Loads a shader from a script tag.
-     * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
-     * @param {string} scriptId The id of the script tag.
-     * @param {number} opt_shaderType The type of shader. If not passed in it will
-     *     be derived from the type of the script tag.
-     * @param {module:webgl-utils.ErrorCallback} opt_errorCallback callback for errors.
-     * @return {WebGLShader} The created shader.
-     */
-    function createShaderFromScript(
-        gl, scriptId, opt_shaderType, opt_errorCallback) {
-      let shaderSource = '';
-      let shaderType;
-      const shaderScript = document.getElementById(scriptId);
-      if (!shaderScript) {
-        throw ('*** Error: unknown script element' + scriptId);
-      }
-      shaderSource = shaderScript.text;
-  
-      if (!opt_shaderType) {
-        if (shaderScript.type === 'x-shader/x-vertex') {
-          shaderType = gl.VERTEX_SHADER;
-        } else if (shaderScript.type === 'x-shader/x-fragment') {
-          shaderType = gl.FRAGMENT_SHADER;
-        } else if (shaderType !== gl.VERTEX_SHADER && shaderType !== gl.FRAGMENT_SHADER) {
-          throw ('*** Error: unknown shader type');
-        }
-      }
-  
-      return loadShader(
-          gl, shaderSource, opt_shaderType ? opt_shaderType : shaderType,
-          opt_errorCallback);
-    }
-  
-    const defaultShaderType = [
-      'VERTEX_SHADER',
-      'FRAGMENT_SHADER',
-    ];
-  
-    /**
-     * Creates a program from 2 script tags.
-     *
-     * @param {WebGLRenderingContext} gl The WebGLRenderingContext
-     *        to use.
-     * @param {string[]} shaderScriptIds Array of ids of the script
-     *        tags for the shaders. The first is assumed to be the
-     *        vertex shader, the second the fragment shader.
-     * @param {string[]} [opt_attribs] An array of attribs names. Locations will be assigned by index if not passed in
-     * @param {number[]} [opt_locations] The locations for the. A parallel array to opt_attribs letting you assign locations.
-     * @param {module:webgl-utils.ErrorCallback} opt_errorCallback callback for errors. By default it just prints an error to the console
-     *        on error. If you want something else pass an callback. It's passed an error message.
-     * @return {WebGLProgram} The created program.
-     * @memberOf module:webgl-utils
-     */
-    function createProgramFromScripts(
-        gl, shaderScriptIds, opt_attribs, opt_locations, opt_errorCallback) {
-      const shaders = [];
-      for (let ii = 0; ii < shaderScriptIds.length; ++ii) {
-        shaders.push(createShaderFromScript(
-            gl, shaderScriptIds[ii], gl[defaultShaderType[ii]], opt_errorCallback));
-      }
-      return createProgram(gl, shaders, opt_attribs, opt_locations, opt_errorCallback);
-    }
-  
-    /**
-     * Creates a program from 2 sources.
-     *
-     * @param {WebGLRenderingContext} gl The WebGLRenderingContext
-     *        to use.
-     * @param {string[]} shaderSourcess Array of sources for the
-     *        shaders. The first is assumed to be the vertex shader,
-     *        the second the fragment shader.
-     * @param {string[]} [opt_attribs] An array of attribs names. Locations will be assigned by index if not passed in
-     * @param {number[]} [opt_locations] The locations for the. A parallel array to opt_attribs letting you assign locations.
-     * @param {module:webgl-utils.ErrorCallback} opt_errorCallback callback for errors. By default it just prints an error to the console
-     *        on error. If you want something else pass an callback. It's passed an error message.
-     * @return {WebGLProgram} The created program.
-     * @memberOf module:webgl-utils
-     */
-    function createProgramFromSources(
-        gl, shaderSources, opt_attribs, opt_locations, opt_errorCallback) {
-      const shaders = [];
-      for (let ii = 0; ii < shaderSources.length; ++ii) {
-        shaders.push(loadShader(
-            gl, shaderSources[ii], gl[defaultShaderType[ii]], opt_errorCallback));
-      }
-      return createProgram(gl, shaders, opt_attribs, opt_locations, opt_errorCallback);
-    }
-  
-    /**
-     * Returns the corresponding bind point for a given sampler type
-     */
-    function getBindPointForSamplerType(gl, type) {
-      if (type === gl.SAMPLER_2D)   return gl.TEXTURE_2D;        // eslint-disable-line
-      if (type === gl.SAMPLER_CUBE) return gl.TEXTURE_CUBE_MAP;  // eslint-disable-line
-      return undefined;
-    }
-  
-    /**
-     * @typedef {Object.<string, function>} Setters
-     */
-  
-    /**
-     * Creates setter functions for all uniforms of a shader
-     * program.
-     *
-     * @see {@link module:webgl-utils.setUniforms}
-     *
-     * @param {WebGLProgram} program the program to create setters for.
-     * @returns {Object.<string, function>} an object with a setter by name for each uniform
-     * @memberOf module:webgl-utils
-     */
-    function createUniformSetters(gl, program) {
-      let textureUnit = 0;
-  
-      /**
-       * Creates a setter for a uniform of the given program with it's
-       * location embedded in the setter.
-       * @param {WebGLProgram} program
-       * @param {WebGLUniformInfo} uniformInfo
-       * @returns {function} the created setter.
-       */
-      function createUniformSetter(program, uniformInfo) {
-        const location = gl.getUniformLocation(program, uniformInfo.name);
-        const type = uniformInfo.type;
-        // Check if this uniform is an array
-        const isArray = (uniformInfo.size > 1 && uniformInfo.name.substr(-3) === '[0]');
-        if (type === gl.FLOAT && isArray) {
-          return function(v) {
-            gl.uniform1fv(location, v);
-          };
-        }
-        if (type === gl.FLOAT) {
-          return function(v) {
-            gl.uniform1f(location, v);
-          };
-        }
-        if (type === gl.FLOAT_VEC2) {
-          return function(v) {
-            gl.uniform2fv(location, v);
-          };
-        }
-        if (type === gl.FLOAT_VEC3) {
-          return function(v) {
-            gl.uniform3fv(location, v);
-          };
-        }
-        if (type === gl.FLOAT_VEC4) {
-          return function(v) {
-            gl.uniform4fv(location, v);
-          };
-        }
-        if (type === gl.INT && isArray) {
-          return function(v) {
-            gl.uniform1iv(location, v);
-          };
-        }
-        if (type === gl.INT) {
-          return function(v) {
-            gl.uniform1i(location, v);
-          };
-        }
-        if (type === gl.INT_VEC2) {
-          return function(v) {
-            gl.uniform2iv(location, v);
-          };
-        }
-        if (type === gl.INT_VEC3) {
-          return function(v) {
-            gl.uniform3iv(location, v);
-          };
-        }
-        if (type === gl.INT_VEC4) {
-          return function(v) {
-            gl.uniform4iv(location, v);
-          };
-        }
-        if (type === gl.BOOL) {
-          return function(v) {
-            gl.uniform1iv(location, v);
-          };
-        }
-        if (type === gl.BOOL_VEC2) {
-          return function(v) {
-            gl.uniform2iv(location, v);
-          };
-        }
-        if (type === gl.BOOL_VEC3) {
-          return function(v) {
-            gl.uniform3iv(location, v);
-          };
-        }
-        if (type === gl.BOOL_VEC4) {
-          return function(v) {
-            gl.uniform4iv(location, v);
-          };
-        }
-        if (type === gl.FLOAT_MAT2) {
-          return function(v) {
-            gl.uniformMatrix2fv(location, false, v);
-          };
-        }
-        if (type === gl.FLOAT_MAT3) {
-          return function(v) {
-            gl.uniformMatrix3fv(location, false, v);
-          };
-        }
-        if (type === gl.FLOAT_MAT4) {
-          return function(v) {
-            gl.uniformMatrix4fv(location, false, v);
-          };
-        }
-        if ((type === gl.SAMPLER_2D || type === gl.SAMPLER_CUBE) && isArray) {
-          const units = [];
-          for (let ii = 0; ii < info.size; ++ii) {
-            units.push(textureUnit++);
-          }
-          return function(bindPoint, units) {
-            return function(textures) {
-              gl.uniform1iv(location, units);
-              textures.forEach(function(texture, index) {
-                gl.activeTexture(gl.TEXTURE0 + units[index]);
-                gl.bindTexture(bindPoint, texture);
-              });
-            };
-          }(getBindPointForSamplerType(gl, type), units);
-        }
-        if (type === gl.SAMPLER_2D || type === gl.SAMPLER_CUBE) {
-          return function(bindPoint, unit) {
-            return function(texture) {
-              gl.uniform1i(location, unit);
-              gl.activeTexture(gl.TEXTURE0 + unit);
-              gl.bindTexture(bindPoint, texture);
-            };
-          }(getBindPointForSamplerType(gl, type), textureUnit++);
-        }
-        throw ('unknown type: 0x' + type.toString(16)); // we should never get here.
-      }
-  
-      const uniformSetters = { };
-      const numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
-  
-      for (let ii = 0; ii < numUniforms; ++ii) {
-        const uniformInfo = gl.getActiveUniform(program, ii);
-        if (!uniformInfo) {
-          break;
-        }
-        let name = uniformInfo.name;
-        // remove the array suffix.
-        if (name.substr(-3) === '[0]') {
-          name = name.substr(0, name.length - 3);
-        }
-        const setter = createUniformSetter(program, uniformInfo);
-        uniformSetters[name] = setter;
-      }
-      return uniformSetters;
-    }
-  
-    /**
-     * Set uniforms and binds related textures.
-     *
-     * Example:
-     *
-     *     let programInfo = createProgramInfo(
-     *         gl, ["some-vs", "some-fs"]);
-     *
-     *     let tex1 = gl.createTexture();
-     *     let tex2 = gl.createTexture();
-     *
-     *     ... assume we setup the textures with data ...
-     *
-     *     let uniforms = {
-     *       u_someSampler: tex1,
-     *       u_someOtherSampler: tex2,
-     *       u_someColor: [1,0,0,1],
-     *       u_somePosition: [0,1,1],
-     *       u_someMatrix: [
-     *         1,0,0,0,
-     *         0,1,0,0,
-     *         0,0,1,0,
-     *         0,0,0,0,
-     *       ],
-     *     };
-     *
-     *     gl.useProgram(program);
-     *
-     * This will automatically bind the textures AND set the
-     * uniforms.
-     *
-     *     setUniforms(programInfo.uniformSetters, uniforms);
-     *
-     * For the example above it is equivalent to
-     *
-     *     let texUnit = 0;
-     *     gl.activeTexture(gl.TEXTURE0 + texUnit);
-     *     gl.bindTexture(gl.TEXTURE_2D, tex1);
-     *     gl.uniform1i(u_someSamplerLocation, texUnit++);
-     *     gl.activeTexture(gl.TEXTURE0 + texUnit);
-     *     gl.bindTexture(gl.TEXTURE_2D, tex2);
-     *     gl.uniform1i(u_someSamplerLocation, texUnit++);
-     *     gl.uniform4fv(u_someColorLocation, [1, 0, 0, 1]);
-     *     gl.uniform3fv(u_somePositionLocation, [0, 1, 1]);
-     *     gl.uniformMatrix4fv(u_someMatrix, false, [
-     *         1,0,0,0,
-     *         0,1,0,0,
-     *         0,0,1,0,
-     *         0,0,0,0,
-     *       ]);
-     *
-     * Note it is perfectly reasonable to call `setUniforms` multiple times. For example
-     *
-     *     let uniforms = {
-     *       u_someSampler: tex1,
-     *       u_someOtherSampler: tex2,
-     *     };
-     *
-     *     let moreUniforms {
-     *       u_someColor: [1,0,0,1],
-     *       u_somePosition: [0,1,1],
-     *       u_someMatrix: [
-     *         1,0,0,0,
-     *         0,1,0,0,
-     *         0,0,1,0,
-     *         0,0,0,0,
-     *       ],
-     *     };
-     *
-     *     setUniforms(programInfo.uniformSetters, uniforms);
-     *     setUniforms(programInfo.uniformSetters, moreUniforms);
-     *
-     * @param {Object.<string, function>|module:webgl-utils.ProgramInfo} setters the setters returned from
-     *        `createUniformSetters` or a ProgramInfo from {@link module:webgl-utils.createProgramInfo}.
-     * @param {Object.<string, value>} an object with values for the
-     *        uniforms.
-     * @memberOf module:webgl-utils
-     */
-    function setUniforms(setters, ...values) {
-      setters = setters.uniformSetters || setters;
-      for (const uniforms of values) {
-        Object.keys(uniforms).forEach(function(name) {
-          const setter = setters[name];
-          if (setter) {
-            setter(uniforms[name]);
-          }
-        });
-      }
-    }
-  
-    /**
-     * Creates setter functions for all attributes of a shader
-     * program. You can pass this to {@link module:webgl-utils.setBuffersAndAttributes} to set all your buffers and attributes.
-     *
-     * @see {@link module:webgl-utils.setAttributes} for example
-     * @param {WebGLProgram} program the program to create setters for.
-     * @return {Object.<string, function>} an object with a setter for each attribute by name.
-     * @memberOf module:webgl-utils
-     */
-    function createAttributeSetters(gl, program) {
-      const attribSetters = {
-      };
-  
-      function createAttribSetter(index) {
-        return function(b) {
-            if (b.value) {
-              gl.disableVertexAttribArray(index);
-              switch (b.value.length) {
-                case 4:
-                  gl.vertexAttrib4fv(index, b.value);
-                  break;
-                case 3:
-                  gl.vertexAttrib3fv(index, b.value);
-                  break;
-                case 2:
-                  gl.vertexAttrib2fv(index, b.value);
-                  break;
-                case 1:
-                  gl.vertexAttrib1fv(index, b.value);
-                  break;
-                default:
-                  throw new Error('the length of a float constant value must be between 1 and 4!');
-              }
-            } else {
-              gl.bindBuffer(gl.ARRAY_BUFFER, b.buffer);
-              gl.enableVertexAttribArray(index);
-              gl.vertexAttribPointer(
-                  index, b.numComponents || b.size, b.type || gl.FLOAT, b.normalize || false, b.stride || 0, b.offset || 0);
-            }
-          };
-      }
-  
-      const numAttribs = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
-      for (let ii = 0; ii < numAttribs; ++ii) {
-        const attribInfo = gl.getActiveAttrib(program, ii);
-        if (!attribInfo) {
-          break;
-        }
-        const index = gl.getAttribLocation(program, attribInfo.name);
-        attribSetters[attribInfo.name] = createAttribSetter(index);
-      }
-  
-      return attribSetters;
-    }
-  
-    /**
-     * Sets attributes and binds buffers (deprecated... use {@link module:webgl-utils.setBuffersAndAttributes})
-     *
-     * Example:
-     *
-     *     let program = createProgramFromScripts(
-     *         gl, ["some-vs", "some-fs"]);
-     *
-     *     let attribSetters = createAttributeSetters(program);
-     *
-     *     let positionBuffer = gl.createBuffer();
-     *     let texcoordBuffer = gl.createBuffer();
-     *
-     *     let attribs = {
-     *       a_position: {buffer: positionBuffer, numComponents: 3},
-     *       a_texcoord: {buffer: texcoordBuffer, numComponents: 2},
-     *     };
-     *
-     *     gl.useProgram(program);
-     *
-     * This will automatically bind the buffers AND set the
-     * attributes.
-     *
-     *     setAttributes(attribSetters, attribs);
-     *
-     * Properties of attribs. For each attrib you can add
-     * properties:
-     *
-     * *   type: the type of data in the buffer. Default = gl.FLOAT
-     * *   normalize: whether or not to normalize the data. Default = false
-     * *   stride: the stride. Default = 0
-     * *   offset: offset into the buffer. Default = 0
-     *
-     * For example if you had 3 value float positions, 2 value
-     * float texcoord and 4 value uint8 colors you'd setup your
-     * attribs like this
-     *
-     *     let attribs = {
-     *       a_position: {buffer: positionBuffer, numComponents: 3},
-     *       a_texcoord: {buffer: texcoordBuffer, numComponents: 2},
-     *       a_color: {
-     *         buffer: colorBuffer,
-     *         numComponents: 4,
-     *         type: gl.UNSIGNED_BYTE,
-     *         normalize: true,
-     *       },
-     *     };
-     *
-     * @param {Object.<string, function>|model:webgl-utils.ProgramInfo} setters Attribute setters as returned from createAttributeSetters or a ProgramInfo as returned {@link module:webgl-utils.createProgramInfo}
-     * @param {Object.<string, module:webgl-utils.AttribInfo>} attribs AttribInfos mapped by attribute name.
-     * @memberOf module:webgl-utils
-     * @deprecated use {@link module:webgl-utils.setBuffersAndAttributes}
-     */
-    function setAttributes(setters, attribs) {
-      setters = setters.attribSetters || setters;
-      Object.keys(attribs).forEach(function(name) {
-        const setter = setters[name];
-        if (setter) {
-          setter(attribs[name]);
-        }
-      });
-    }
-  
-    /**
-     * Creates a vertex array object and then sets the attributes
-     * on it
-     *
-     * @param {WebGLRenderingContext} gl The WebGLRenderingContext
-     *        to use.
-     * @param {Object.<string, function>} setters Attribute setters as returned from createAttributeSetters
-     * @param {Object.<string, module:webgl-utils.AttribInfo>} attribs AttribInfos mapped by attribute name.
-     * @param {WebGLBuffer} [indices] an optional ELEMENT_ARRAY_BUFFER of indices
-     */
-    function createVAOAndSetAttributes(gl, setters, attribs, indices) {
-      const vao = gl.createVertexArray();
-      gl.bindVertexArray(vao);
-      setAttributes(setters, attribs);
-      if (indices) {
-        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indices);
-      }
-      // We unbind this because otherwise any change to ELEMENT_ARRAY_BUFFER
-      // like when creating buffers for other stuff will mess up this VAO's binding
-      gl.bindVertexArray(null);
-      return vao;
-    }
-  
-    /**
-     * Creates a vertex array object and then sets the attributes
-     * on it
-     *
-     * @param {WebGLRenderingContext} gl The WebGLRenderingContext
-     *        to use.
-     * @param {Object.<string, function>| module:webgl-utils.ProgramInfo} programInfo as returned from createProgramInfo or Attribute setters as returned from createAttributeSetters
-     * @param {module:webgl-utils:BufferInfo} bufferInfo BufferInfo as returned from createBufferInfoFromArrays etc...
-     * @param {WebGLBuffer} [indices] an optional ELEMENT_ARRAY_BUFFER of indices
-     */
-    function createVAOFromBufferInfo(gl, programInfo, bufferInfo) {
-      return createVAOAndSetAttributes(gl, programInfo.attribSetters || programInfo, bufferInfo.attribs, bufferInfo.indices);
-    }
-  
-    /**
-     * @typedef {Object} ProgramInfo
-     * @property {WebGLProgram} program A shader program
-     * @property {Object<string, function>} uniformSetters: object of setters as returned from createUniformSetters,
-     * @property {Object<string, function>} attribSetters: object of setters as returned from createAttribSetters,
-     * @memberOf module:webgl-utils
-     */
-  
-    /**
-     * Creates a ProgramInfo from 2 sources.
-     *
-     * A ProgramInfo contains
-     *
-     *     programInfo = {
-     *        program: WebGLProgram,
-     *        uniformSetters: object of setters as returned from createUniformSetters,
-     *        attribSetters: object of setters as returned from createAttribSetters,
-     *     }
-     *
-     * @param {WebGLRenderingContext} gl The WebGLRenderingContext
-     *        to use.
-     * @param {string[]} shaderSourcess Array of sources for the
-     *        shaders or ids. The first is assumed to be the vertex shader,
-     *        the second the fragment shader.
-     * @param {string[]} [opt_attribs] An array of attribs names. Locations will be assigned by index if not passed in
-     * @param {number[]} [opt_locations] The locations for the. A parallel array to opt_attribs letting you assign locations.
-     * @param {module:webgl-utils.ErrorCallback} opt_errorCallback callback for errors. By default it just prints an error to the console
-     *        on error. If you want something else pass an callback. It's passed an error message.
-     * @return {module:webgl-utils.ProgramInfo} The created program.
-     * @memberOf module:webgl-utils
-     */
-    function createProgramInfo(
-        gl, shaderSources, opt_attribs, opt_locations, opt_errorCallback) {
-      shaderSources = shaderSources.map(function(source) {
-        const script = document.getElementById(source);
-        return script ? script.text : source;
-      });
-      const program = webglUtils.createProgramFromSources(gl, shaderSources, opt_attribs, opt_locations, opt_errorCallback);
-      if (!program) {
-        return null;
-      }
-      const uniformSetters = createUniformSetters(gl, program);
-      const attribSetters = createAttributeSetters(gl, program);
-      return {
-        program: program,
-        uniformSetters: uniformSetters,
-        attribSetters: attribSetters,
-      };
-    }
-  
-    /**
-     * Sets attributes and buffers including the `ELEMENT_ARRAY_BUFFER` if appropriate
-     *
-     * Example:
-     *
-     *     let programInfo = createProgramInfo(
-     *         gl, ["some-vs", "some-fs"]);
-     *
-     *     let arrays = {
-     *       position: { numComponents: 3, data: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0], },
-     *       texcoord: { numComponents: 2, data: [0, 0, 0, 1, 1, 0, 1, 1],                 },
-     *     };
-     *
-     *     let bufferInfo = createBufferInfoFromArrays(gl, arrays);
-     *
-     *     gl.useProgram(programInfo.program);
-     *
-     * This will automatically bind the buffers AND set the
-     * attributes.
-     *
-     *     setBuffersAndAttributes(programInfo.attribSetters, bufferInfo);
-     *
-     * For the example above it is equivilent to
-     *
-     *     gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
-     *     gl.enableVertexAttribArray(a_positionLocation);
-     *     gl.vertexAttribPointer(a_positionLocation, 3, gl.FLOAT, false, 0, 0);
-     *     gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
-     *     gl.enableVertexAttribArray(a_texcoordLocation);
-     *     gl.vertexAttribPointer(a_texcoordLocation, 4, gl.FLOAT, false, 0, 0);
-     *
-     * @param {WebGLRenderingContext} gl A WebGLRenderingContext.
-     * @param {Object.<string, function>} setters Attribute setters as returned from `createAttributeSetters`
-     * @param {module:webgl-utils.BufferInfo} buffers a BufferInfo as returned from `createBufferInfoFromArrays`.
-     * @memberOf module:webgl-utils
-     */
-    function setBuffersAndAttributes(gl, setters, buffers) {
-      setAttributes(setters, buffers.attribs);
-      if (buffers.indices) {
-        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices);
-      }
-    }
-  
-    // Add your prefix here.
-    const browserPrefixes = [
-      '',
-      'MOZ_',
-      'OP_',
-      'WEBKIT_',
-    ];
-  
-    /**
-     * Given an extension name like WEBGL_compressed_texture_s3tc
-     * returns the supported version extension, like
-     * WEBKIT_WEBGL_compressed_teture_s3tc
-     * @param {string} name Name of extension to look for
-     * @return {WebGLExtension} The extension or undefined if not
-     *     found.
-     * @memberOf module:webgl-utils
-     */
-    function getExtensionWithKnownPrefixes(gl, name) {
-      for (let ii = 0; ii < browserPrefixes.length; ++ii) {
-        const prefixedName = browserPrefixes[ii] + name;
-        const ext = gl.getExtension(prefixedName);
-        if (ext) {
-          return ext;
-        }
-      }
-      return undefined;
-    }
-  
-    /**
-     * Resize a canvas to match the size its displayed.
-     * @param {HTMLCanvasElement} canvas The canvas to resize.
-     * @param {number} [multiplier] amount to multiply by.
-     *    Pass in window.devicePixelRatio for native pixels.
-     * @return {boolean} true if the canvas was resized.
-     * @memberOf module:webgl-utils
-     */
-    function resizeCanvasToDisplaySize(canvas, multiplier) {
-      multiplier = multiplier || 1;
-      const width  = canvas.clientWidth  * multiplier | 0;
-      const height = canvas.clientHeight * multiplier | 0;
-      if (canvas.width !== width ||  canvas.height !== height) {
-        canvas.width  = width;
-        canvas.height = height;
-        return true;
-      }
-      return false;
-    }
-  
-    // Add `push` to a typed array. It just keeps a 'cursor'
-    // and allows use to `push` values into the array so we
-    // don't have to manually compute offsets
-    function augmentTypedArray(typedArray, numComponents) {
-      let cursor = 0;
-      typedArray.push = function() {
-        for (let ii = 0; ii < arguments.length; ++ii) {
-          const value = arguments[ii];
-          if (value instanceof Array || (value.buffer && value.buffer instanceof ArrayBuffer)) {
-            for (let jj = 0; jj < value.length; ++jj) {
-              typedArray[cursor++] = value[jj];
-            }
-          } else {
-            typedArray[cursor++] = value;
-          }
-        }
-      };
-      typedArray.reset = function(opt_index) {
-        cursor = opt_index || 0;
-      };
-      typedArray.numComponents = numComponents;
-      Object.defineProperty(typedArray, 'numElements', {
-        get: function() {
-          return this.length / this.numComponents | 0;
-        },
-      });
-      return typedArray;
-    }
-  
-    /**
-     * creates a typed array with a `push` function attached
-     * so that you can easily *push* values.
-     *
-     * `push` can take multiple arguments. If an argument is an array each element
-     * of the array will be added to the typed array.
-     *
-     * Example:
-     *
-     *     let array = createAugmentedTypedArray(3, 2);  // creates a Float32Array with 6 values
-     *     array.push(1, 2, 3);
-     *     array.push([4, 5, 6]);
-     *     // array now contains [1, 2, 3, 4, 5, 6]
-     *
-     * Also has `numComponents` and `numElements` properties.
-     *
-     * @param {number} numComponents number of components
-     * @param {number} numElements number of elements. The total size of the array will be `numComponents * numElements`.
-     * @param {constructor} opt_type A constructor for the type. Default = `Float32Array`.
-     * @return {ArrayBuffer} A typed array.
-     * @memberOf module:webgl-utils
-     */
-    function createAugmentedTypedArray(numComponents, numElements, opt_type) {
-      const Type = opt_type || Float32Array;
-      return augmentTypedArray(new Type(numComponents * numElements), numComponents);
-    }
-  
-    function createBufferFromTypedArray(gl, array, type, drawType) {
-      type = type || gl.ARRAY_BUFFER;
-      const buffer = gl.createBuffer();
-      gl.bindBuffer(type, buffer);
-      gl.bufferData(type, array, drawType || gl.STATIC_DRAW);
-      return buffer;
-    }
-  
-    function allButIndices(name) {
-      return name !== 'indices';
-    }
-  
-    function createMapping(obj) {
-      const mapping = {};
-      Object.keys(obj).filter(allButIndices).forEach(function(key) {
-        mapping['a_' + key] = key;
-      });
-      return mapping;
-    }
-  
-    function getGLTypeForTypedArray(gl, typedArray) {
-      if (typedArray instanceof Int8Array)    { return gl.BYTE; }            // eslint-disable-line
-      if (typedArray instanceof Uint8Array)   { return gl.UNSIGNED_BYTE; }   // eslint-disable-line
-      if (typedArray instanceof Int16Array)   { return gl.SHORT; }           // eslint-disable-line
-      if (typedArray instanceof Uint16Array)  { return gl.UNSIGNED_SHORT; }  // eslint-disable-line
-      if (typedArray instanceof Int32Array)   { return gl.INT; }             // eslint-disable-line
-      if (typedArray instanceof Uint32Array)  { return gl.UNSIGNED_INT; }    // eslint-disable-line
-      if (typedArray instanceof Float32Array) { return gl.FLOAT; }           // eslint-disable-line
-      throw 'unsupported typed array type';
-    }
-  
-    // This is really just a guess. Though I can't really imagine using
-    // anything else? Maybe for some compression?
-    function getNormalizationForTypedArray(typedArray) {
-      if (typedArray instanceof Int8Array)    { return true; }  // eslint-disable-line
-      if (typedArray instanceof Uint8Array)   { return true; }  // eslint-disable-line
-      return false;
-    }
-  
-    function isArrayBuffer(a) {
-      return a.buffer && a.buffer instanceof ArrayBuffer;
-    }
-  
-    function guessNumComponentsFromName(name, length) {
-      let numComponents;
-      if (name.indexOf('coord') >= 0) {
-        numComponents = 2;
-      } else if (name.indexOf('color') >= 0) {
-        numComponents = 4;
-      } else {
-        numComponents = 3;  // position, normals, indices ...
-      }
-  
-      if (length % numComponents > 0) {
-        throw 'can not guess numComponents. You should specify it.';
-      }
-  
-      return numComponents;
-    }
-  
-    function makeTypedArray(array, name) {
-      if (isArrayBuffer(array)) {
-        return array;
-      }
-  
-      if (array.data && isArrayBuffer(array.data)) {
-        return array.data;
-      }
-  
-      if (Array.isArray(array)) {
-        array = {
-          data: array,
-        };
-      }
-  
-      if (!array.numComponents) {
-        array.numComponents = guessNumComponentsFromName(name, array.length);
-      }
-  
-      let type = array.type;
-      if (!type) {
-        if (name === 'indices') {
-          type = Uint16Array;
-        }
-      }
-      const typedArray = createAugmentedTypedArray(array.numComponents, array.data.length / array.numComponents | 0, type);
-      typedArray.push(array.data);
-      return typedArray;
-    }
-  
-    /**
-     * @typedef {Object} AttribInfo
-     * @property {number} [numComponents] the number of components for this attribute.
-     * @property {number} [size] the number of components for this attribute.
-     * @property {number} [type] the type of the attribute (eg. `gl.FLOAT`, `gl.UNSIGNED_BYTE`, etc...) Default = `gl.FLOAT`
-     * @property {boolean} [normalized] whether or not to normalize the data. Default = false
-     * @property {number} [offset] offset into buffer in bytes. Default = 0
-     * @property {number} [stride] the stride in bytes per element. Default = 0
-     * @property {WebGLBuffer} buffer the buffer that contains the data for this attribute
-     * @memberOf module:webgl-utils
-     */
-  
-  
-    /**
-     * Creates a set of attribute data and WebGLBuffers from set of arrays
-     *
-     * Given
-     *
-     *      let arrays = {
-     *        position: { numComponents: 3, data: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0], },
-     *        texcoord: { numComponents: 2, data: [0, 0, 0, 1, 1, 0, 1, 1],                 },
-     *        normal:   { numComponents: 3, data: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1],     },
-     *        color:    { numComponents: 4, data: [255, 255, 255, 255, 255, 0, 0, 255, 0, 0, 255, 255], type: Uint8Array, },
-     *        indices:  { numComponents: 3, data: [0, 1, 2, 1, 2, 3],                       },
-     *      };
-     *
-     * returns something like
-     *
-     *      let attribs = {
-     *        a_position: { numComponents: 3, type: gl.FLOAT,         normalize: false, buffer: WebGLBuffer, },
-     *        a_texcoord: { numComponents: 2, type: gl.FLOAT,         normalize: false, buffer: WebGLBuffer, },
-     *        a_normal:   { numComponents: 3, type: gl.FLOAT,         normalize: false, buffer: WebGLBuffer, },
-     *        a_color:    { numComponents: 4, type: gl.UNSIGNED_BYTE, normalize: true,  buffer: WebGLBuffer, },
-     *      };
-     *
-     * @param {WebGLRenderingContext} gl The webgl rendering context.
-     * @param {Object.<string, array|typedarray>} arrays The arrays
-     * @param {Object.<string, string>} [opt_mapping] mapping from attribute name to array name.
-     *     if not specified defaults to "a_name" -> "name".
-     * @return {Object.<string, module:webgl-utils.AttribInfo>} the attribs
-     * @memberOf module:webgl-utils
-     */
-    function createAttribsFromArrays(gl, arrays, opt_mapping) {
-      const mapping = opt_mapping || createMapping(arrays);
-      const attribs = {};
-      Object.keys(mapping).forEach(function(attribName) {
-        const bufferName = mapping[attribName];
-        const origArray = arrays[bufferName];
-        if (origArray.value) {
-          attribs[attribName] = {
-            value: origArray.value,
-          };
-        } else {
-          const array = makeTypedArray(origArray, bufferName);
-          attribs[attribName] = {
-            buffer:        createBufferFromTypedArray(gl, array),
-            numComponents: origArray.numComponents || array.numComponents || guessNumComponentsFromName(bufferName),
-            type:          getGLTypeForTypedArray(gl, array),
-            normalize:     getNormalizationForTypedArray(array),
-          };
-        }
-      });
-      return attribs;
-    }
-  
-    function getArray(array) {
-      return array.length ? array : array.data;
-    }
-  
-    const texcoordRE = /coord|texture/i;
-    const colorRE = /color|colour/i;
-  
-    function guessNumComponentsFromName(name, length) {
-      let numComponents;
-      if (texcoordRE.test(name)) {
-        numComponents = 2;
-      } else if (colorRE.test(name)) {
-        numComponents = 4;
-      } else {
-        numComponents = 3;  // position, normals, indices ...
-      }
-  
-      if (length % numComponents > 0) {
-        throw new Error(`Can not guess numComponents for attribute '${name}'. Tried ${numComponents} but ${length} values is not evenly divisible by ${numComponents}. You should specify it.`);
-      }
-  
-      return numComponents;
-    }
-  
-    function getNumComponents(array, arrayName) {
-      return array.numComponents || array.size || guessNumComponentsFromName(arrayName, getArray(array).length);
-    }
-  
-    /**
-     * tries to get the number of elements from a set of arrays.
-     */
-    const positionKeys = ['position', 'positions', 'a_position'];
-    function getNumElementsFromNonIndexedArrays(arrays) {
-      let key;
-      for (const k of positionKeys) {
-        if (k in arrays) {
-          key = k;
-          break;
-        }
-      }
-      key = key || Object.keys(arrays)[0];
-      const array = arrays[key];
-      const length = getArray(array).length;
-      const numComponents = getNumComponents(array, key);
-      const numElements = length / numComponents;
-      if (length % numComponents > 0) {
-        throw new Error(`numComponents ${numComponents} not correct for length ${length}`);
-      }
-      return numElements;
-    }
-  
-    /**
-     * @typedef {Object} BufferInfo
-     * @property {number} numElements The number of elements to pass to `gl.drawArrays` or `gl.drawElements`.
-     * @property {WebGLBuffer} [indices] The indices `ELEMENT_ARRAY_BUFFER` if any indices exist.
-     * @property {Object.<string, module:webgl-utils.AttribInfo>} attribs The attribs approriate to call `setAttributes`
-     * @memberOf module:webgl-utils
-     */
-  
-  
-    /**
-     * Creates a BufferInfo from an object of arrays.
-     *
-     * This can be passed to {@link module:webgl-utils.setBuffersAndAttributes} and to
-     * {@link module:webgl-utils:drawBufferInfo}.
-     *
-     * Given an object like
-     *
-     *     let arrays = {
-     *       position: { numComponents: 3, data: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0], },
-     *       texcoord: { numComponents: 2, data: [0, 0, 0, 1, 1, 0, 1, 1],                 },
-     *       normal:   { numComponents: 3, data: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1],     },
-     *       indices:  { numComponents: 3, data: [0, 1, 2, 1, 2, 3],                       },
-     *     };
-     *
-     *  Creates an BufferInfo like this
-     *
-     *     bufferInfo = {
-     *       numElements: 4,        // or whatever the number of elements is
-     *       indices: WebGLBuffer,  // this property will not exist if there are no indices
-     *       attribs: {
-     *         a_position: { buffer: WebGLBuffer, numComponents: 3, },
-     *         a_normal:   { buffer: WebGLBuffer, numComponents: 3, },
-     *         a_texcoord: { buffer: WebGLBuffer, numComponents: 2, },
-     *       },
-     *     };
-     *
-     *  The properties of arrays can be JavaScript arrays in which case the number of components
-     *  will be guessed.
-     *
-     *     let arrays = {
-     *        position: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0],
-     *        texcoord: [0, 0, 0, 1, 1, 0, 1, 1],
-     *        normal:   [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1],
-     *        indices:  [0, 1, 2, 1, 2, 3],
-     *     };
-     *
-     *  They can also by TypedArrays
-     *
-     *     let arrays = {
-     *        position: new Float32Array([0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0]),
-     *        texcoord: new Float32Array([0, 0, 0, 1, 1, 0, 1, 1]),
-     *        normal:   new Float32Array([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]),
-     *        indices:  new Uint16Array([0, 1, 2, 1, 2, 3]),
-     *     };
-     *
-     *  Or augmentedTypedArrays
-     *
-     *     let positions = createAugmentedTypedArray(3, 4);
-     *     let texcoords = createAugmentedTypedArray(2, 4);
-     *     let normals   = createAugmentedTypedArray(3, 4);
-     *     let indices   = createAugmentedTypedArray(3, 2, Uint16Array);
-     *
-     *     positions.push([0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0]);
-     *     texcoords.push([0, 0, 0, 1, 1, 0, 1, 1]);
-     *     normals.push([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]);
-     *     indices.push([0, 1, 2, 1, 2, 3]);
-     *
-     *     let arrays = {
-     *        position: positions,
-     *        texcoord: texcoords,
-     *        normal:   normals,
-     *        indices:  indices,
-     *     };
-     *
-     * For the last example it is equivalent to
-     *
-     *     let bufferInfo = {
-     *       attribs: {
-     *         a_position: { numComponents: 3, buffer: gl.createBuffer(), },
-     *         a_texcoods: { numComponents: 2, buffer: gl.createBuffer(), },
-     *         a_normals: { numComponents: 3, buffer: gl.createBuffer(), },
-     *       },
-     *       indices: gl.createBuffer(),
-     *       numElements: 6,
-     *     };
-     *
-     *     gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.a_position.buffer);
-     *     gl.bufferData(gl.ARRAY_BUFFER, arrays.position, gl.STATIC_DRAW);
-     *     gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.a_texcoord.buffer);
-     *     gl.bufferData(gl.ARRAY_BUFFER, arrays.texcoord, gl.STATIC_DRAW);
-     *     gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.a_normal.buffer);
-     *     gl.bufferData(gl.ARRAY_BUFFER, arrays.normal, gl.STATIC_DRAW);
-     *     gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bufferInfo.indices);
-     *     gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, arrays.indices, gl.STATIC_DRAW);
-     *
-     * @param {WebGLRenderingContext} gl A WebGLRenderingContext
-     * @param {Object.<string, array|object|typedarray>} arrays Your data
-     * @param {Object.<string, string>} [opt_mapping] an optional mapping of attribute to array name.
-     *    If not passed in it's assumed the array names will be mapped to an attribute
-     *    of the same name with "a_" prefixed to it. An other words.
-     *
-     *        let arrays = {
-     *           position: ...,
-     *           texcoord: ...,
-     *           normal:   ...,
-     *           indices:  ...,
-     *        };
-     *
-     *        bufferInfo = createBufferInfoFromArrays(gl, arrays);
-     *
-     *    Is the same as
-     *
-     *        let arrays = {
-     *           position: ...,
-     *           texcoord: ...,
-     *           normal:   ...,
-     *           indices:  ...,
-     *        };
-     *
-     *        let mapping = {
-     *          a_position: "position",
-     *          a_texcoord: "texcoord",
-     *          a_normal:   "normal",
-     *        };
-     *
-     *        bufferInfo = createBufferInfoFromArrays(gl, arrays, mapping);
-     *
-     * @return {module:webgl-utils.BufferInfo} A BufferInfo
-     * @memberOf module:webgl-utils
-     */
-    function createBufferInfoFromArrays(gl, arrays, opt_mapping) {
-      const bufferInfo = {
-        attribs: createAttribsFromArrays(gl, arrays, opt_mapping),
-      };
-      let indices = arrays.indices;
-      if (indices) {
-        indices = makeTypedArray(indices, 'indices');
-        bufferInfo.indices = createBufferFromTypedArray(gl, indices, gl.ELEMENT_ARRAY_BUFFER);
-        bufferInfo.numElements = indices.length;
-      } else {
-        bufferInfo.numElements = getNumElementsFromNonIndexedArrays(arrays);
-      }
-  
-      return bufferInfo;
-    }
-  
-    /**
-     * Creates buffers from typed arrays
-     *
-     * Given something like this
-     *
-     *     let arrays = {
-     *        positions: [1, 2, 3],
-     *        normals: [0, 0, 1],
-     *     }
-     *
-     * returns something like
-     *
-     *     buffers = {
-     *       positions: WebGLBuffer,
-     *       normals: WebGLBuffer,
-     *     }
-     *
-     * If the buffer is named 'indices' it will be made an ELEMENT_ARRAY_BUFFER.
-     *
-     * @param {WebGLRenderingContext} gl A WebGLRenderingContext.
-     * @param {Object<string, array|typedarray>} arrays
-     * @return {Object<string, WebGLBuffer>} returns an object with one WebGLBuffer per array
-     * @memberOf module:webgl-utils
-     */
-    function createBuffersFromArrays(gl, arrays) {
-      const buffers = { };
-      Object.keys(arrays).forEach(function(key) {
-        const type = key === 'indices' ? gl.ELEMENT_ARRAY_BUFFER : gl.ARRAY_BUFFER;
-        const array = makeTypedArray(arrays[key], name);
-        buffers[key] = createBufferFromTypedArray(gl, array, type);
-      });
-  
-      // hrm
-      if (arrays.indices) {
-        buffers.numElements = arrays.indices.length;
-      } else if (arrays.position) {
-        buffers.numElements = arrays.position.length / 3;
-      }
-  
-      return buffers;
-    }
-  
-    /**
-     * Calls `gl.drawElements` or `gl.drawArrays`, whichever is appropriate
-     *
-     * normally you'd call `gl.drawElements` or `gl.drawArrays` yourself
-     * but calling this means if you switch from indexed data to non-indexed
-     * data you don't have to remember to update your draw call.
-     *
-     * @param {WebGLRenderingContext} gl A WebGLRenderingContext
-     * @param {module:webgl-utils.BufferInfo} bufferInfo as returned from createBufferInfoFromArrays
-     * @param {enum} [primitiveType] eg (gl.TRIANGLES, gl.LINES, gl.POINTS, gl.TRIANGLE_STRIP, ...)
-     * @param {number} [count] An optional count. Defaults to bufferInfo.numElements
-     * @param {number} [offset] An optional offset. Defaults to 0.
-     * @memberOf module:webgl-utils
-     */
-    function drawBufferInfo(gl, bufferInfo, primitiveType, count, offset) {
-      const indices = bufferInfo.indices;
-      primitiveType = primitiveType === undefined ? gl.TRIANGLES : primitiveType;
-      const numElements = count === undefined ? bufferInfo.numElements : count;
-      offset = offset === undefined ? 0 : offset;
-      if (indices) {
-        gl.drawElements(primitiveType, numElements, gl.UNSIGNED_SHORT, offset);
-      } else {
-        gl.drawArrays(primitiveType, offset, numElements);
-      }
-    }
-  
-    /**
-     * @typedef {Object} DrawObject
-     * @property {module:webgl-utils.ProgramInfo} programInfo A ProgramInfo as returned from createProgramInfo
-     * @property {module:webgl-utils.BufferInfo} bufferInfo A BufferInfo as returned from createBufferInfoFromArrays
-     * @property {Object<string, ?>} uniforms The values for the uniforms
-     * @memberOf module:webgl-utils
-     */
-  
-    /**
-     * Draws a list of objects
-     * @param {WebGLRenderingContext} gl A WebGLRenderingContext
-     * @param {DrawObject[]} objectsToDraw an array of objects to draw.
-     * @memberOf module:webgl-utils
-     */
-    function drawObjectList(gl, objectsToDraw) {
-      let lastUsedProgramInfo = null;
-      let lastUsedBufferInfo = null;
-  
-      objectsToDraw.forEach(function(object) {
-        const programInfo = object.programInfo;
-        const bufferInfo = object.bufferInfo;
-        let bindBuffers = false;
-  
-        if (programInfo !== lastUsedProgramInfo) {
-          lastUsedProgramInfo = programInfo;
-          gl.useProgram(programInfo.program);
-          bindBuffers = true;
-        }
-  
-        // Setup all the needed attributes.
-        if (bindBuffers || bufferInfo !== lastUsedBufferInfo) {
-          lastUsedBufferInfo = bufferInfo;
-          setBuffersAndAttributes(gl, programInfo.attribSetters, bufferInfo);
-        }
-  
-        // Set the uniforms.
-        setUniforms(programInfo.uniformSetters, object.uniforms);
-  
-        // Draw
-        drawBufferInfo(gl, bufferInfo);
-      });
-    }
-  
-    function glEnumToString(gl, v) {
-      const results = [];
-      for (const key in gl) {
-        if (gl[key] === v) {
-          results.push(key);
-        }
-      }
-      return results.length
-          ? results.join(' | ')
-          : `0x${v.toString(16)}`;
-    }
-  
-    const isIE = /*@cc_on!@*/false || !!document.documentMode;
-    // Edge 20+
-    const isEdge = !isIE && !!window.StyleMedia;
-    if (isEdge) {
-      // Hack for Edge. Edge's WebGL implmentation is crap still and so they
-      // only respond to "experimental-webgl". I don't want to clutter the
-      // examples with that so his hack works around it
-      HTMLCanvasElement.prototype.getContext = function(origFn) {
-        return function() {
-          let args = arguments;
-          const type = args[0];
-          if (type === 'webgl') {
-            args = [].slice.call(arguments);
-            args[0] = 'experimental-webgl';
-          }
-          return origFn.apply(this, args);
-        };
-      }(HTMLCanvasElement.prototype.getContext);
-    }
-  
-    return {
-      createAugmentedTypedArray: createAugmentedTypedArray,
-      createAttribsFromArrays: createAttribsFromArrays,
-      createBuffersFromArrays: createBuffersFromArrays,
-      createBufferInfoFromArrays: createBufferInfoFromArrays,
-      createAttributeSetters: createAttributeSetters,
-      createProgram: createProgram,
-      createProgramFromScripts: createProgramFromScripts,
-      createProgramFromSources: createProgramFromSources,
-      createProgramInfo: createProgramInfo,
-      createUniformSetters: createUniformSetters,
-      createVAOAndSetAttributes: createVAOAndSetAttributes,
-      createVAOFromBufferInfo: createVAOFromBufferInfo,
-      drawBufferInfo: drawBufferInfo,
-      drawObjectList: drawObjectList,
-      glEnumToString: glEnumToString,
-      getExtensionWithKnownPrefixes: getExtensionWithKnownPrefixes,
-      resizeCanvasToDisplaySize: resizeCanvasToDisplaySize,
-      setAttributes: setAttributes,
-      setBuffersAndAttributes: setBuffersAndAttributes,
-      setUniforms: setUniforms,
-    };
-  
-  }));
-  
\ No newline at end of file