docs: function Program(gl, codes, includes) {
      if (!typeof codes.vert === 'string') {
          throw Error('[tfw.webgl.Program] Missing attribute `vert` in argument `codes`!');
      }
      if (!typeof codes.frag === 'string') {
          throw Error('[tfw.webgl.Program] Missing attribute `frag` in argument `codes`!');
      }
  
      codes = parseIncludes( codes, includes );
  
      var shaderProgram = gl.createProgram();
      gl.attachShader(shaderProgram, getVertexShader(gl, codes.vert || '//No Vertex Shader'));
      gl.attachShader(shaderProgram, getFragmentShader(gl, codes.frag || '//No Fragment Shader'));
      gl.linkProgram(shaderProgram);
  
      this.program = shaderProgram;
      Object.freeze( this.program );
  
      this.use = function() {
          gl.useProgram(shaderProgram);
      };
      this.use();
  
      var index, item;
      var attribs = {};
      var attribsCount = gl.getProgramParameter(shaderProgram, gl.ACTIVE_ATTRIBUTES);
      for (index = 0; index < attribsCount; index++) {
          item = gl.getActiveAttrib( shaderProgram, index );
          attribs[item.name] = gl.getAttribLocation(shaderProgram, item.name);
          this['$' + item.name] = gl.getAttribLocation(shaderProgram, item.name);
      }
  
      Object.freeze(attribs);
      this.attribs = attribs;
      var uniforms = {};
      var uniformsCount = gl.getProgramParameter(shaderProgram, gl.ACTIVE_UNIFORMS);
      for (index = 0; index < uniformsCount; index++) {
          item = gl.getActiveUniform( shaderProgram, index );
          uniforms[item.name] = gl.getUniformLocation(shaderProgram, item.name);
          Object.defineProperty(this, '$' + item.name, {
              set: createUniformSetter(gl, item, uniforms[item.name]),
              get: createUniformGetter(item),
              enumerable: true,
              configurable: true
          });
      }
      Object.freeze(uniforms);
      this.uniforms = uniforms;
  }