function createTextures(gl, textureOptions, callback) {
  callback = callback || noop;
  var numDownloading = 0;
  var errors = [];
  var textures = {};
  var images = {};

  function callCallbackIfReady() {
    if (numDownloading === 0) {
      setTimeout(function () {
        callback(errors.length ? errors : undefined, textures, images);
      }, 0);
    }
  }

  Object.keys(textureOptions).forEach(function (name) {
    var options = textureOptions[name];
    var onLoadFn;

    if (isAsyncSrc(options.src)) {
      onLoadFn = function onLoadFn(err, tex, img) {
        images[name] = img;
        --numDownloading;

        if (err) {
          errors.push(err);
        }

        callCallbackIfReady();
      };

      ++numDownloading;
    }

    textures[name] = createTexture(gl, options, onLoadFn);
  }); // queue the callback if there are no images to download.
  // We do this because if your code is structured to wait for
  // images to download but then you comment out all the async
  // images your code would break.

  callCallbackIfReady();
  return textures;
}
Creates a bunch of textures based on the passed in options.

Example:

    const textures = twgl.createTextures(gl, {
      // a power of 2 image
      hftIcon: { src: "images/hft-icon-16.png", mag: gl.NEAREST },
      // a non-power of 2 image
      clover: { src: "images/clover.jpg" },
      // From a canvas
      fromCanvas: { src: ctx.canvas },
      // A cubemap from 6 images
      yokohama: {
        target: gl.TEXTURE_CUBE_MAP,
        src: [
          'images/yokohama/posx.jpg',
          'images/yokohama/negx.jpg',
          'images/yokohama/posy.jpg',
          'images/yokohama/negy.jpg',
          'images/yokohama/posz.jpg',
          'images/yokohama/negz.jpg',
        ],
      },
      // A cubemap from 1 image (can be 1x6, 2x3, 3x2, 6x1)
      goldengate: {
        target: gl.TEXTURE_CUBE_MAP,
        src: 'images/goldengate.jpg',
      },
      // A 2x2 pixel texture from a JavaScript array
      checker: {
        mag: gl.NEAREST,
        min: gl.LINEAR,
        src: [
          255,255,255,255,
          192,192,192,255,
          192,192,192,255,
          255,255,255,255,
        ],
      },
      // a 1x2 pixel texture from a typed array.
      stripe: {
        mag: gl.NEAREST,
        min: gl.LINEAR,
        format: gl.LUMINANCE,
        src: new Uint8Array([
          255,
          128,
          255,
          128,
          255,
          128,
          255,
          128,
        ]),
        width: 1,
      },
    });

Now

*   `textures.hftIcon` will be a 2d texture
*   `textures.clover` will be a 2d texture
*   `textures.fromCanvas` will be a 2d texture
*   `textures.yohohama` will be a cubemap texture
*   `textures.goldengate` will be a cubemap texture
*   `textures.checker` will be a 2d texture
*   `textures.stripe` will be a 2d texture

@param {WebGLRenderingContext} gl the WebGLRenderingContext
@param {Object.<string,module:twgl.TextureOptions>} options A object of TextureOptions one per texture.
@param {module:twgl.TexturesReadyCallback} [callback] A callback called when all textures have been downloaded.
@return {Object.<string,WebGLTexture>} the created textures by name
@memberOf module:twgl/textures