Textures from code - Web APIs

Textures from code
==================

* « Previous
* Next »

This WebGL example provides a simple demonstration of procedural texturing with fragment shaders. That is, using code to generate textures for use in shading WebGL objects.

Drawing textures with code
--------------------------

Texturing a point sprite with calculations done per-pixel in the fragment shader.
```

<p>Texture from code. Simple demonstration of procedural texturing</p>
```

```
<canvas>Your browser does not seem to support canvases.</canvas>
```

```
body {
  text-align: center;
}
canvas {
  width: 280px;
  height: 210px;
  margin: auto;
  padding: 0;
  border: none;
  background-color: black;
}
button {
  display: block;
  font-size: inherit;
  margin: auto;
  padding: 0.6em;
}
```

```html
<script type="x-shader/x-vertex" id="vertex-shader">
 #version 100
 precision highp float;

 attribute vec2 position;

 void main() {
 gl\_Position = vec4(position, 0.0, 1.0);
 gl\_PointSize = 128.0;
 }
</script>
```

```html
<script type="x-shader/x-fragment" id="fragment-shader">
 #version 100
 precision mediump float;
 void main() {
 vec2 fragmentPosition = 2.0\*gl\_PointCoord - 1.0;
 float distance = length(fragmentPosition);
 float distanceSqrd = distance \* distance;
 gl\_FragColor = vec4(
 0.2/distanceSqrd,
 0.1/distanceSqrd,
 0.0, 1.0 );
 }
</script>
```

```
;(() => {
  "use strict";
```

```js
window.addEventListener("load", setupWebGL, false);

let gl;
let program;

function setupWebGL(evt) {
  window.removeEventListener(evt.type, setupWebGL, false);
  if (!(gl = getRenderingContext())) return;

  let source = document.querySelector("#vertex-shader").innerHTML;
  const vertexShader = gl.createShader(gl.VERTEX\_SHADER);
  gl.shaderSource(vertexShader, source);
  gl.compileShader(vertexShader);

  source = document.querySelector("#fragment-shader").innerHTML;
  const fragmentShader = gl.createShader(gl.FRAGMENT\_SHADER);
  gl.shaderSource(fragmentShader, source);
  gl.compileShader(fragmentShader);

  program = gl.createProgram();
  gl.attachShader(program, vertexShader);
  gl.attachShader(program, fragmentShader);
  gl.linkProgram(program);
  gl.detachShader(program, vertexShader);
  gl.detachShader(program, fragmentShader);
  gl.deleteShader(vertexShader);
  gl.deleteShader(fragmentShader);
  if (!gl.getProgramParameter(program, gl.LINK\_STATUS)) {
    const linkErrLog = gl.getProgramInfoLog(program);
    cleanup();
    document.querySelector("p").textContent =
      `Shader program did not link successfully. Error log: ${linkErrLog}`;
    return;
  }
  initializeAttributes();
  gl.useProgram(program);
  gl.drawArrays(gl.POINTS, 0, 1);
  cleanup();
}

let buffer;
function initializeAttributes() {
  gl.enableVertexAttribArray(0);
  buffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY\_BUFFER, buffer);
  gl.bufferData(gl.ARRAY\_BUFFER, new Float32Array([0.0, 0.0]), gl.STATIC\_DRAW);
  gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
}

function cleanup() {
  gl.useProgram(null);
  if (buffer) {
    gl.deleteBuffer(buffer);
  }
  if (program) {
    gl.deleteProgram(program);
  }
}
```

```
function getRenderingContext() {
  const canvas = document.querySelector("canvas");
  canvas.width = canvas.clientWidth;
  canvas.height = canvas.clientHeight;
  const gl =
    canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
  if (!gl) {
    const paragraph = document.querySelector("p");
    paragraph.textContent =
      "Failed. Your browser or device may not support WebGL.";
    return null;
  }
  gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.clear(gl.COLOR\_BUFFER\_BIT);
  return gl;
}
```

```
})();
```

The source code of this example is also available on GitHub.

* « Previous
* Next »


Hello GLSL - Web APIs

Hello GLSL
==========

* « Previous
* Next »

This WebGL example demonstrates a very basic GLSL shader program that draws a solid color square.

**Note:** This example will most likely work in all modern desktop browsers. But it may not work in some mobile or older browsers. If the canvas remains blank, you can check the output of the next example, which draws exactly the same thing. But remember to read through the explanations and code on this page, before moving on to the next.

Hello World program in GLSL
---------------------------

A very simple first shader program.
```

<p>Hello World! Hello GLSL!</p>
```

```
<canvas>Your browser does not seem to support HTML canvas.</canvas>
```

```
body {
  text-align: center;
}
canvas {
  width: 280px;
  height: 210px;
  margin: auto;
  padding: 0;
  border: none;
  background-color: black;
}
button {
  display: block;
  font-size: inherit;
  margin: auto;
  padding: 0.6em;
}
```

```html
<script type="x-shader/x-vertex" id="vertex-shader">
 #version 100
 void main() {
 gl\_Position = vec4(0.0, 0.0, 0.0, 1.0);
 gl\_PointSize = 64.0;
 }
</script>
```

```html
<script type="x-shader/x-fragment" id="fragment-shader">
 #version 100
 void main() {
 gl\_FragColor = vec4(0.18, 0.54, 0.34, 1.0);
 }
</script>
```

```
;(() => {
  "use strict";
```

```js
window.addEventListener("load", setupWebGL, false);
let gl;
let program;

function setupWebGL(evt) {
  window.removeEventListener(evt.type, setupWebGL, false);
  if (!(gl = getRenderingContext())) return;

  let source = document.querySelector("#vertex-shader").innerHTML;
  const vertexShader = gl.createShader(gl.VERTEX\_SHADER);
  gl.shaderSource(vertexShader, source);
  gl.compileShader(vertexShader);

  source = document.querySelector("#fragment-shader").innerHTML;
  const fragmentShader = gl.createShader(gl.FRAGMENT\_SHADER);
  gl.shaderSource(fragmentShader, source);
  gl.compileShader(fragmentShader);
  program = gl.createProgram();
  gl.attachShader(program, vertexShader);
  gl.attachShader(program, fragmentShader);
  gl.linkProgram(program);
  gl.detachShader(program, vertexShader);
  gl.detachShader(program, fragmentShader);
  gl.deleteShader(vertexShader);
  gl.deleteShader(fragmentShader);
  if (!gl.getProgramParameter(program, gl.LINK\_STATUS)) {
    const linkErrLog = gl.getProgramInfoLog(program);
    cleanup();
    document.querySelector("p").textContent =
      `Shader program did not link successfully. Error log: ${linkErrLog}`;
    return;
  }

  initializeAttributes();

  gl.useProgram(program);
  gl.drawArrays(gl.POINTS, 0, 1);

  cleanup();
}

let buffer;
function initializeAttributes() {
  gl.enableVertexAttribArray(0);
  buffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY\_BUFFER, buffer);
  gl.vertexAttribPointer(0, 1, gl.FLOAT, false, 0, 0);
}

function cleanup() {
  gl.useProgram(null);
  if (buffer) {
    gl.deleteBuffer(buffer);
  }
  if (program) {
    gl.deleteProgram(program);
  }
}
```

```
function getRenderingContext() {
  const canvas = document.querySelector("canvas");
  canvas.width = canvas.clientWidth;
  canvas.height = canvas.clientHeight;
  const gl =
    canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
  if (!gl) {
    const paragraph = document.querySelector("p");
    paragraph.textContent =
      "Failed. Your browser or device may not support WebGL.";
    return null;
  }
  gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.clear(gl.COLOR\_BUFFER\_BIT);
  return gl;
}
```

```
})();
```

The source code of this example is also available on GitHub.

* « Previous
* Next »


Video textures - Web APIs

Video textures
==============

* « Previous

This example demonstrates how to use video files as textures for WebGL surfaces.

Textures from video
-------------------

The source code of this example is available on GitHub.

* « Previous


Raining rectangles - Web APIs

Raining rectangles
==================

* « Previous
* Next »

A simple WebGL game that demonstrates clearing with solid colors, scissoring, animation, and user interaction.

Animation and user interaction with scissoring
----------------------------------------------

This is a simple game. The objective: try to catch as many of the raining rectangles as you can by clicking on them. In this example, we use an object-oriented approach for the displayed rectangles, which helps to keep the state of the rectangle (its position, color, and so on) organized in one place, and the overall code more compact and reusable.

This example combines clearing the drawing buffer with solid colors and scissoring operations. It is a preview of a full graphical application that manipulates various phases of the WebGL graphics pipeline and state machine.

In addition, the example demonstrates how to integrate the WebGL function calls within a game loop. The game loop is responsible for drawing the animation frames, and keeping the animation responsive to user input. Here, the game loop is implemented using timeouts.
```

<p>You caught <strong>0</strong>. You missed <strong>0</strong>.</p>
```

```
<canvas>Your browser does not seem to support canvases.</canvas>
```

```
body {
  text-align: center;
}
canvas {
  display: block;
  width: 280px;
  height: 210px;
  margin: auto;
  padding: 0;
  border: none;
  background-color: black;
}
button {
  display: block;
  font-size: inherit;
  margin: auto;
  padding: 0.6em;
}
```

```
;(() => {
  "use strict";
```

```js
window.addEventListener("load", setupAnimation, false);
let gl;
let timer;
let rainingRect;
let scoreDisplay;
let missesDisplay;
function setupAnimation(evt) {
  window.removeEventListener(evt.type, setupAnimation, false);
  if (!(gl = getRenderingContext())) return;
  gl.enable(gl.SCISSOR\_TEST);

  rainingRect = new Rectangle();
  timer = setTimeout(drawAnimation, 17);
  document
    .querySelector("canvas")
    .addEventListener("click", playerClick, false);
  [scoreDisplay, missesDisplay] = document.querySelectorAll("strong");
}

let score = 0;
let misses = 0;
function drawAnimation() {
  gl.scissor(
    rainingRect.position[0],
    rainingRect.position[1],
    rainingRect.size[0],
    rainingRect.size[1],
  );
  gl.clear(gl.COLOR\_BUFFER\_BIT);
  rainingRect.position[1] -= rainingRect.velocity;
  if (rainingRect.position[1] < 0) {
    misses += 1;
    missesDisplay.textContent = misses;
    rainingRect = new Rectangle();
  }
  // We are using setTimeout for animation. So we reschedule
  // the timeout to call drawAnimation again in 17ms.
  // Otherwise we won't get any animation.
  timer = setTimeout(drawAnimation, 17);
}

function playerClick(evt) {
  // We need to transform the position of the click event from
  // window coordinates to relative position inside the canvas.
  // In addition we need to remember that vertical position in
  // WebGL increases from bottom to top, unlike in the browser
  // window.
  const position = [
    evt.pageX - evt.target.offsetLeft,
    gl.drawingBufferHeight - (evt.pageY - evt.target.offsetTop),
  ];
  // If the click falls inside the rectangle, we caught it.

  // Increment score and create a new rectangle.
  const diffPos = [
    position[0] - rainingRect.position[0],
    position[1] - rainingRect.position[1],
  ];
  if (
    diffPos[0] >= 0 &&
    diffPos[0] < rainingRect.size[0] &&
    diffPos[1] >= 0 &&
    diffPos[1] < rainingRect.size[1]
  ) {
    score += 1;
    scoreDisplay.textContent = score;
    rainingRect = new Rectangle();
  }
}

function Rectangle() {
  // Keeping a reference to the new Rectangle object, rather
  // than using the confusing this keyword.
  const rect = this;
  // We get three random numbers and use them for new rectangle
  // size and position. For each we use a different number,
  // because we want horizontal size, vertical size and
  // position to be determined independently.
  const randNums = getRandomVector();
  rect.size = [5 + 120 \* randNums[0], 5 + 120 \* randNums[1]];
  rect.position = [
    randNums[2] \* (gl.drawingBufferWidth - rect.size[0]),
    gl.drawingBufferHeight,
  ];
  rect.velocity = 1.0 + 6.0 \* Math.random();
  rect.color = getRandomVector();
  gl.clearColor(rect.color[0], rect.color[1], rect.color[2], 1.0);
  function getRandomVector() {
    return [Math.random(), Math.random(), Math.random()];
  }
}
```

```
function getRenderingContext() {
  const canvas = document.querySelector("canvas");
  canvas.width = canvas.clientWidth;
  canvas.height = canvas.clientHeight;
  const gl =
    canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
  if (!gl) {
    const paragraph = document.querySelector("p");
    paragraph.textContent =
      "Failed. Your browser or device may not support WebGL.";
    return null;
  }
  gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.clear(gl.COLOR\_BUFFER\_BIT);
  return gl;
}
```

```
})();
```

The source code of this example is also available on GitHub.

* « Previous
* Next »


Color masking - Web APIs

Color masking
=============

* « Previous
* Next »

This WebGL example modifies random colors by applying color masking to limit the range of displayed colors to specific shades.

Masking random colors
---------------------

This example modifies the random color animation by applying color masking with `colorMask()`. You can think of the color masking operation as if looking at the colored canvas through some tinted glass or color filter. So, by masking off the blue and green channels, you are only allowing the red component of pixels to be updated, and therefore it is as if you were looking through a red tinted glass.

Color masking allows us to demonstrate some basics of color theory. By masking off some channel(s), we are in fact biasing the displayed colors towards the complementary color. So, clearly masking both blue and red, would give us shades of green. Masking only the blue channel would give us shades of yellow (including shades of orange, brown, olive and yellow-green), the complementary of blue. Similarly, masking only green would give us shades of magenta (also purples, crimsons, and so on), and masking only red would give shades of cyan (also sea greens, blues, and so on).

Note that the calls to `colorMask()` only occur when the user clicks on one of the toggle buttons. But rendering is done every second, using the timer. The color mask state of WebGL is preserved, so we do not need to call `colorMask()` every frame to set up the color mask. This is an important aspect of the WebGL state machine. It allows us to set up WebGL in a single initialization phase, and then just execute drawing commands for each frame.

Color masking gives you fine control of updating pixel values on the screen. By limiting the color channels that are written by each drawing command, you can use each channel, for example, to store a different grayscale image. Alternatively, you may use the RGB components for color, but the alpha component for some custom pixel data of your invention.

Finally, color masking teaches us that WebGL is not only a state machine, it is also a *graphics pipeline*. This means that graphics operations in WebGL are done in a certain order, where the output of each operation serves as the input of the next. So, for example, clearing operation sets the value of each pixel to the chosen clear color. Masking occurs later in the pipeline, and modifies the pixel color value, so the final result on the screen is that of the clear color, tinted by the color mask.

```html
<p>Tinting the displayed colors with color masking.</p>
<canvas>Your browser does not seem to support HTML canvas.</canvas>
<button id="red-toggle">On</button>
<button id="green-toggle">On</button>
<button id="blue-toggle">On</button>
```

```css
body {
  text-align: center;
}
canvas {
  display: block;
  width: 280px;
  height: 210px;
  margin: auto;
  padding: 0;
  border: none;
  background-color: black;
}
button {
  display: inline-block;
  font-family: serif;
  font-size: inherit;
  font-weight: 900;
  color: white;
  margin: auto;
  padding: 0.6em 1.2em;
}
#red-toggle {
  background-color: red;
}
#green-toggle {
  background-color: green;
}
#blue-toggle {
  background-color: blue;
}
```

```js
window.addEventListener(
  "load",
  function setupAnimation(evt) {
    "use strict";
    window.removeEventListener(evt.type, setupAnimation, false);

    const canvas = document.querySelector("canvas");
    const gl =
      canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
    if (!gl) {
      document.querySelector("p").textContent =
        "Failed to get WebGL context. Your browser or device may not support WebGL.";
      return;
    }
    gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);

    const timer = setInterval(drawAnimation, 1000);

    const mask = [true, true, true];
    const redtoggle = document.querySelector("#red-toggle");
    const greentoggle = document.querySelector("#green-toggle");
    const bluetoggle = document.querySelector("#blue-toggle");
    redtoggle.addEventListener("click", setColorMask, false);
    greentoggle.addEventListener("click", setColorMask, false);
    bluetoggle.addEventListener("click", setColorMask, false);

    function setColorMask(evt) {
      const index =
        (evt.target === greentoggle && 1) ||
        (evt.target === bluetoggle && 2) ||
        0;
      mask[index] = !mask[index];
      evt.target.textContent = mask[index] ? "On" : "Off";
      gl.colorMask(mask[0], mask[1], mask[2], true);
      drawAnimation();
    }

    function drawAnimation() {
      const color = getRandomColor();
      gl.clearColor(color[0], color[1], color[2], 1.0);
      gl.clear(gl.COLOR\_BUFFER\_BIT);
    }

    function getRandomColor() {
      return [Math.random(), Math.random(), Math.random()];
    }
  },
  false,
);
```

The source code of this example is also available on GitHub.

* « Previous
* Next »


Canvas size and WebGL - Web APIs

Canvas size and WebGL
=====================

* « Previous
* Next »

This WebGL example explores the effect of setting (or not setting) the canvas size to its element size in CSS pixels, as it appears in the browser window.

Effect of canvas size on rendering with WebGL
---------------------------------------------

With `scissor()` and `clear()` we can demonstrate how the WebGL drawing buffer is affected by the size of the canvas.

The size of the first canvas is set to the styled `Element` size, determined by CSS. This is done by assigning the `width` and `height` properties of the canvas to the values of the `clientWidth` and `clientHeight` properties, respectively.

In contrast, no such assignment is done for the second canvas. The internal `width` and `height` properties of the canvas remain at default values, which are different than the actual size of the canvas `Element` in the browser window.

The effect is clearly visible when using `scissor()` and `clear()` to draw a square in the center of the canvas, by specifying its position and size in pixels. In the first canvas, we get the desired result. In the second, the square has the wrong shape, size, and position.

```html
<p>Compare the two canvases.</p>
<canvas>Your browser does not seem to support HTML canvas.</canvas>
<canvas>Your browser does not seem to support HTML canvas.</canvas>
```

```css
body {
  text-align: center;
}
canvas {
  display: inline-block;
  width: 120px;
  height: 80px;
  margin: auto;
  padding: 0;
  border: none;
  background-color: black;
}
```

```js
window.addEventListener(
  "load",
  () => {
    const [firstCanvas, secondCanvas] = document.getElementsByTagName("canvas");
    firstCanvas.width = firstCanvas.clientWidth;
    firstCanvas.height = firstCanvas.clientHeight;
    [firstCanvas, secondCanvas].forEach((canvas) => {
      const gl =
        canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
      if (!gl) {
        document.querySelector("p").textContent =
          "Failed. Your browser or device may not support WebGL.";
        return;
      }
      gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
      gl.enable(gl.SCISSOR\_TEST);
      gl.scissor(30, 10, 60, 60);
      gl.clearColor(1.0, 1.0, 0.0, 1.0);
      gl.clear(gl.COLOR\_BUFFER\_BIT);
    });
  },
  false,
);
```

The source code of this example is also available on GitHub.

* « Previous
* Next »


Animating textures in WebGL - Web APIs

Animating textures in WebGL
===========================

* « Previous

In this demonstration, we build upon the previous example by replacing our static textures with the frames of an mp4 video file that's playing. This is actually pretty easy to do and fun to watch, so let's get started. You can use similar code to use any sort of data (such as a `<canvas>`) as the source for your textures.

Getting access to the video
---------------------------

The first step is to create the `<video>` element that we'll use to retrieve the video frames.

**Note:** Add this declaration to that start of your "webgl-demo.js" script:

```js
// will set to true when video can be copied to texture
let copyVideo = false;
```

**Note:** Add this function your "webgl-demo.js" script:

```js
function setupVideo(url) {
  const video = document.createElement("video");

  let playing = false;
  let timeupdate = false;

  video.playsInline = true;
  video.muted = true;
  video.loop = true;

  // Waiting for these 2 events ensures
  // there is data in the video

  video.addEventListener(
    "playing",
    () => {
      playing = true;
      checkReady();
    },
    true,
  );

  video.addEventListener(
    "timeupdate",
    () => {
      timeupdate = true;
      checkReady();
    },
    true,
  );

  video.src = url;
  video.play();

  function checkReady() {
    if (playing && timeupdate) {
      copyVideo = true;
    }
  }

  return video;
}
```

First we create a video element. We set it to autoplay, mute the sound, and loop the video. We then set up two events to make sure the video is playing and the time has been updated. We need both of these checks because it will produce an error if you upload a video to WebGL that has no data available yet. Checking for both of these events guarantees there is data available and it's safe to start uploading video to a WebGL texture. In the code above, we confirm whether we got both of those events; if so, we set a global variable, `copyVideo`, to true to indicate that it's safe to start copying the video to a texture.

And finally, we set the `src` attribute to start and call `play` to start loading and playing the video.

The video must be loaded from a secure source in order to be used to provide texture data to WebGL. That means that you'll not only need to deploy code like using a secure web server, but you'll need a secure server to test with as well. See How do you set up a local testing server? for help.

Using the video frames as a texture
-----------------------------------

The next change is to initialize the texture, which becomes much simpler, since we no longer need to load an image file. Instead, we create an empty texture object, put a single pixel in it, and set its filtering for later use.

**Note:** Replace the `loadTexture()` function in "webgl-demo.js" with the following code:

```js
function initTexture(gl) {
  const texture = gl.createTexture();
  gl.bindTexture(gl.TEXTURE\_2D, texture);

  // Because video has to be download over the internet
  // they might take a moment until it's ready so
  // put a single pixel in the texture so we can
  // use it immediately.
  const level = 0;
  const internalFormat = gl.RGBA;
  const width = 1;
  const height = 1;
  const border = 0;
  const srcFormat = gl.RGBA;
  const srcType = gl.UNSIGNED\_BYTE;
  const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue
  gl.texImage2D(
    gl.TEXTURE\_2D,
    level,
    internalFormat,
    width,
    height,
    border,
    srcFormat,
    srcType,
    pixel,
  );

  // Turn off mips and set wrapping to clamp to edge so it
  // will work regardless of the dimensions of the video.
  gl.texParameteri(gl.TEXTURE\_2D, gl.TEXTURE\_WRAP\_S, gl.CLAMP\_TO\_EDGE);
  gl.texParameteri(gl.TEXTURE\_2D, gl.TEXTURE\_WRAP\_T, gl.CLAMP\_TO\_EDGE);
  gl.texParameteri(gl.TEXTURE\_2D, gl.TEXTURE\_MIN\_FILTER, gl.LINEAR);

  return texture;
}
```

**Note:** Add the following function to "webgl-demo.js":

```js
function updateTexture(gl, texture, video) {
  const level = 0;
  const internalFormat = gl.RGBA;
  const srcFormat = gl.RGBA;
  const srcType = gl.UNSIGNED\_BYTE;
  gl.bindTexture(gl.TEXTURE\_2D, texture);
  gl.texImage2D(
    gl.TEXTURE\_2D,
    level,
    internalFormat,
    srcFormat,
    srcType,
    video,
  );
}
```

You've seen this code before. It's nearly identical to the image onload function in the previous example — except when we call `texImage2D()`, instead of passing an `Image` object, we pass in the `<video>` element. WebGL knows how to pull the current frame out and use it as a texture.

Next, we need to call these new functions from our `main()` function.

**Note:** In your `main()` function, replace the call to `loadTexture()` with this code:

```js
const texture = initTexture(gl);
const video = setupVideo("Firefox.mp4");
```

**Note:** You'll also need to download the Firefox.mp4 file to the same local directory as your JavaScript files.

**Note:** In your `main()` function, replace the `render()` function with this:

```js
// Draw the scene repeatedly
function render(now) {
  now \*= 0.001; // convert to seconds
  deltaTime = now - then;
  then = now;

  if (copyVideo) {
    updateTexture(gl, texture, video);
  }

  drawScene(gl, programInfo, buffers, texture, cubeRotation);
  cubeRotation += deltaTime;

  requestAnimationFrame(render);
}
```

If `copyVideo` is true, we call `updateTexture()` just before we call the `drawScene()` function.

That's all there is to it!

View the complete code | Open this demo on a new page

See also
--------

* Using audio and video in Firefox
* « Previous


Adding 2D content to a WebGL context - Web APIs

Adding 2D content to a WebGL context
====================================

* « Previous
* Next »

Once you've successfully created a WebGL context, you can start rendering into it. A simple thing we can do is draw an untextured square plane, so let's start there.

The complete source code for this project is available on GitHub.

Including the glMatrix library
------------------------------

This project uses the glMatrix library to perform its matrix operations, so you will need to include that in your project. We're loading a copy from a CDN.

**Note:** Update your "index.html" so it looks like this:

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>WebGL Demo</title>
    <link rel="stylesheet" href="./webgl.css" type="text/css" />
    <script
 src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js"
 integrity="sha512-zhHQR0/H5SEBL3Wn6yYSaTTZej12z0hVZKOv3TwCUXT1z5qeqGcXJLLrbERYRScEDDpYIJhPC1fk31gqR783iQ=="
 crossorigin="anonymous"
 defer></script>
    <script src="webgl-demo.js" type="module"></script>
  </head>

  <body>
    <canvas id="glcanvas" width="640" height="480"></canvas>
  </body>
</html>
```

Drawing the scene
-----------------

The most important thing to understand before we get started is that even though we're only rendering a square plane object in this example, we're still drawing in 3D space. It's just we're drawing a square and we're putting it directly in front of the camera perpendicular to the view direction. We need to define the shaders that will create the color for our simple scene as well as draw our object. These will establish how the square plane appears on the screen.

### The shaders

A **shader** is a program, written using the OpenGL ES Shading Language (**GLSL**), that takes information about the vertices that make up a shape and generates the data needed to render the pixels onto the screen: namely, the positions of the pixels and their colors.

There are two shader functions run when drawing WebGL content: the **vertex shader** and the **fragment shader**. You write these in GLSL and pass the text of the code into WebGL to be compiled for execution on the GPU. Together, a set of vertex and fragment shaders is called a **shader program**.

Let's take a quick look at the two types of shader, with the example in mind of drawing a 2D shape into the WebGL context.

#### Vertex shader

Each time a shape is rendered, the vertex shader is run for each vertex in the shape. Its job is to transform the input vertex from its original coordinate system into the **clip space** coordinate system used by WebGL, in which each axis has a range from -1.0 to 1.0, regardless of aspect ratio, actual size, or any other factors.

The vertex shader must perform the needed transforms on the vertex's position, make any other adjustments or calculations it needs to make on a per-vertex basis, then return the transformed vertex by saving it in a special variable provided by GLSL, called `gl_Position`.

The vertex shader can, as needed, also do things like determine the coordinates within the face's texture of the texel to apply to the vertex, apply the normals to determine the lighting factor to apply to the vertex, and so on. This information can then be stored in varyings or attributes as appropriate to be shared with the fragment shader.

Our vertex shader below receives vertex position values from an attribute we define called `aVertexPosition`. That position is then multiplied by two 4x4 matrices we provide called `uProjectionMatrix` and `uModelViewMatrix`; `gl_Position` is set to the result. For more info on projection and other matrixes you might find this article useful.

**Note:** Add this code to your `main()` function:

```js
// Vertex shader program
const vsSource = `
 attribute vec4 aVertexPosition;
 uniform mat4 uModelViewMatrix;
 uniform mat4 uProjectionMatrix;
 void main() {
 gl\_Position = uProjectionMatrix \* uModelViewMatrix \* aVertexPosition;
 }
 `;
```

It's worth noting that we're using a `vec4` attribute for the vertex position, which doesn't actually use a 4-component vector; that is, it could be handled as a `vec2` or `vec3` depending on the situation. But when we do our math, we will need it to be a `vec4`, so rather than convert it to a `vec4` every time we do math, we'll just use a `vec4` from the beginning. This eliminates operations from every calculation we do in our shader. Performance matters.

In this example, we're not computing any lighting at all, since we haven't yet applied any to the scene. That will come later, in the example Lighting in WebGL. Note also the lack of any work with textures here; that will be added in Using textures in WebGL.

#### Fragment shader

The **fragment shader** is called once for every pixel on each shape to be drawn, after the shape's vertices have been processed by the vertex shader. Its job is to determine the color of that pixel by figuring out which texel (that is, the pixel from within the shape's texture) to apply to the pixel, getting that texel's color, then applying the appropriate lighting to the color. The color is then returned to the WebGL layer by storing it in the special variable `gl_FragColor`. That color is then drawn to the screen in the correct position for the shape's corresponding pixel.

In this case, we're returning white every time, since we're just drawing a white square, with no lighting in use.

**Note:** Add this code to your `main()` function:

```js
const fsSource = `
 void main() {
 gl\_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
 }
 `;
```

### Initializing the shaders

Now that we've defined the two shaders we need to pass them to WebGL, compile them, and link them together. The code below creates the two shaders by calling `loadShader()`, passing the type and source for the shader. It then creates a program, attaches the shaders and links them together. If compiling or linking fails the code displays an alert.

**Note:** Add these two functions to your "webgl-demo.js" script:

```js
//
// Initialize a shader program, so WebGL knows how to draw our data
//
function initShaderProgram(gl, vsSource, fsSource) {
  const vertexShader = loadShader(gl, gl.VERTEX\_SHADER, vsSource);
  const fragmentShader = loadShader(gl, gl.FRAGMENT\_SHADER, fsSource);

  // Create the shader program

  const shaderProgram = gl.createProgram();
  gl.attachShader(shaderProgram, vertexShader);
  gl.attachShader(shaderProgram, fragmentShader);
  gl.linkProgram(shaderProgram);

  // If creating the shader program failed, alert

  if (!gl.getProgramParameter(shaderProgram, gl.LINK\_STATUS)) {
    alert(
      `Unable to initialize the shader program: ${gl.getProgramInfoLog(
 shaderProgram,
 )}`,
    );
    return null;
  }

  return shaderProgram;
}

//
// creates a shader of the given type, uploads the source and
// compiles it.
//
function loadShader(gl, type, source) {
  const shader = gl.createShader(type);

  // Send the source to the shader object

  gl.shaderSource(shader, source);

  // Compile the shader program

  gl.compileShader(shader);

  // See if it compiled successfully

  if (!gl.getShaderParameter(shader, gl.COMPILE\_STATUS)) {
    alert(
      `An error occurred compiling the shaders: ${gl.getShaderInfoLog(shader)}`,
    );
    gl.deleteShader(shader);
    return null;
  }

  return shader;
}
```

The `loadShader()` function takes as input the WebGL context, the shader type, and the source code, then creates and compiles the shader as follows:

1. A new shader is created by calling `gl.createShader()`.
2. The shader's source code is sent to the shader by calling `gl.shaderSource()`.
3. Once the shader has the source code, it's compiled using `gl.compileShader()`.
4. To check to be sure the shader successfully compiled, the shader parameter `gl.COMPILE_STATUS` is checked. To get its value, we call `gl.getShaderParameter()`, specifying the shader and the name of the parameter we want to check (`gl.COMPILE_STATUS`). If that's `false`, we know the shader failed to compile, so show an alert with log information obtained from the compiler using `gl.getShaderInfoLog()`, then delete the shader and return `null` to indicate a failure to load the shader.
5. If the shader was loaded and successfully compiled, the compiled shader is returned to the caller.

**Note:** Add this code to your `main()` function:

```js
// Initialize a shader program; this is where all the lighting
// for the vertices and so forth is established.
const shaderProgram = initShaderProgram(gl, vsSource, fsSource);
```

After we've created a shader program we need to look up the locations that WebGL assigned to our inputs. In this case we have one attribute and two uniforms. Attributes receive values from buffers. Each iteration of the vertex shader receives the next value from the buffer assigned to that attribute. Uniforms are similar to JavaScript global variables. They stay the same value for all iterations of a shader. Since the attribute and uniform locations are specific to a single shader program we'll store them together to make them easy to pass around

**Note:** Add this code to your `main()` function:

```js
// Collect all the info needed to use the shader program.
// Look up which attribute our shader program is using
// for aVertexPosition and look up uniform locations.
const programInfo = {
  program: shaderProgram,
  attribLocations: {
    vertexPosition: gl.getAttribLocation(shaderProgram, "aVertexPosition"),
  },
  uniformLocations: {
    projectionMatrix: gl.getUniformLocation(shaderProgram, "uProjectionMatrix"),
    modelViewMatrix: gl.getUniformLocation(shaderProgram, "uModelViewMatrix"),
  },
};
```

Creating the square plane
-------------------------

Before we can render our square plane, we need to create the buffer that contains its vertex positions and put the vertex positions in it.

We'll do that using a function we call `initBuffers()`, which we will implement in a separate JavaScript module. As we explore more advanced WebGL concepts, this module will be augmented to create more — and more complex — 3D objects.

**Note:** Create a new file called "init-buffers.js", and give it the following contents:

```js
function initBuffers(gl) {
  const positionBuffer = initPositionBuffer(gl);

  return {
    position: positionBuffer,
  };
}

function initPositionBuffer(gl) {
  // Create a buffer for the square's positions.
  const positionBuffer = gl.createBuffer();

  // Select the positionBuffer as the one to apply buffer
  // operations to from here out.
  gl.bindBuffer(gl.ARRAY\_BUFFER, positionBuffer);

  // Now create an array of positions for the square.
  const positions = [1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0];

  // Now pass the list of positions into WebGL to build the
  // shape. We do this by creating a Float32Array from the
  // JavaScript array, then use it to fill the current buffer.
  gl.bufferData(gl.ARRAY\_BUFFER, new Float32Array(positions), gl.STATIC\_DRAW);

  return positionBuffer;
}

export { initBuffers };
```

This routine is pretty simplistic given the basic nature of the scene in this example. It starts by calling the `gl` object's `createBuffer()` method to obtain a buffer into which we'll store the vertex positions. This is then bound to the context by calling the `bindBuffer()` method.

Once that's done, we create a JavaScript array containing the position for each vertex of the square plane. This is then converted into an array of floats and passed into the `gl` object's `bufferData()` method to establish the vertex positions for the object.

Rendering the scene
-------------------

Once the shaders are established, the locations are looked up, and the square plane's vertex positions put in a buffer, we can actually render the scene. We'll do this in a `drawScene()` function that, again, we'll implement in a separate JavaScript module.

**Note:** Create a new file called "draw-scene.js", and give it the following contents:

```js
function drawScene(gl, programInfo, buffers) {
  gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque
  gl.clearDepth(1.0); // Clear everything
  gl.enable(gl.DEPTH\_TEST); // Enable depth testing
  gl.depthFunc(gl.LEQUAL); // Near things obscure far things

  // Clear the canvas before we start drawing on it.

  gl.clear(gl.COLOR\_BUFFER\_BIT | gl.DEPTH\_BUFFER\_BIT);

  // Create a perspective matrix, a special matrix that is
  // used to simulate the distortion of perspective in a camera.
  // Our field of view is 45 degrees, with a width/height
  // ratio that matches the display size of the canvas
  // and we only want to see objects between 0.1 units
  // and 100 units away from the camera.

  const fieldOfView = (45 \* Math.PI) / 180; // in radians
  const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
  const zNear = 0.1;
  const zFar = 100.0;
  const projectionMatrix = mat4.create();

  // note: glmatrix.js always has the first argument
  // as the destination to receive the result.
  mat4.perspective(projectionMatrix, fieldOfView, aspect, zNear, zFar);

  // Set the drawing position to the "identity" point, which is
  // the center of the scene.
  const modelViewMatrix = mat4.create();

  // Now move the drawing position a bit to where we want to
  // start drawing the square.
  mat4.translate(
    modelViewMatrix, // destination matrix
    modelViewMatrix, // matrix to translate
    [-0.0, 0.0, -6.0],
  ); // amount to translate

  // Tell WebGL how to pull out the positions from the position
  // buffer into the vertexPosition attribute.
  setPositionAttribute(gl, buffers, programInfo);

  // Tell WebGL to use our program when drawing
  gl.useProgram(programInfo.program);

  // Set the shader uniforms
  gl.uniformMatrix4fv(
    programInfo.uniformLocations.projectionMatrix,
    false,
    projectionMatrix,
  );
  gl.uniformMatrix4fv(
    programInfo.uniformLocations.modelViewMatrix,
    false,
    modelViewMatrix,
  );

  {
    const offset = 0;
    const vertexCount = 4;
    gl.drawArrays(gl.TRIANGLE\_STRIP, offset, vertexCount);
  }
}

// Tell WebGL how to pull out the positions from the position
// buffer into the vertexPosition attribute.
function setPositionAttribute(gl, buffers, programInfo) {
  const numComponents = 2; // pull out 2 values per iteration
  const type = gl.FLOAT; // the data in the buffer is 32bit floats
  const normalize = false; // don't normalize
  const stride = 0; // how many bytes to get from one set of values to the next
  // 0 = use type and numComponents above
  const offset = 0; // how many bytes inside the buffer to start from
  gl.bindBuffer(gl.ARRAY\_BUFFER, buffers.position);
  gl.vertexAttribPointer(
    programInfo.attribLocations.vertexPosition,
    numComponents,
    type,
    normalize,
    stride,
    offset,
  );
  gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition);
}

export { drawScene };
```

The first step is to clear the canvas to our background color; then we establish the camera's perspective. We set a field of view of 45°, with a width to height ratio that matches the display dimensions of our canvas. We also specify that we only want objects between 0.1 and 100 units from the camera to be rendered.

Then we establish the position of the square plane by loading the identity position and translating away from the camera by 6 units. After that, we bind the square's vertex buffer to the attribute the shader is using for `aVertexPosition` and we tell WebGL how to pull the data out of it. Finally we draw the object by calling the `drawArrays()` method.

Finally, let's call `initBuffers()` and `drawScene()`.

**Note:** Add this code to the start of your "webgl-demo.js" file:

```js
import { initBuffers } from "./init-buffers.js";
import { drawScene } from "./draw-scene.js";
```

**Note:** Add this code to the end of your `main()` function:

```js
// Here's where we call the routine that builds all the
// objects we'll be drawing.
const buffers = initBuffers(gl);

// Draw the scene
drawScene(gl, programInfo, buffers);
```

The result should look like this:

View the complete code | Open this demo on a new page

Matrix utility operations
-------------------------

Matrix operations might seem complicated but they are actually pretty simple if you take them one step at a time. Generally people use a matrix library rather than writing their own. In our case we're using the popular glMatrix library.

### See also

* Matrices on WebGLFundamentals
* Matrices on Wolfram MathWorld
* Matrix on Wikipedia
* « Previous
* Next »


Using shaders to apply color in WebGL - Web APIs

Using shaders to apply color in WebGL
=====================================

* « Previous
* Next »

Having created a square plane in the previous demonstration, the next obvious step is to add a splash of color to it. We can do this by revising the shaders.

Applying color to the vertices
------------------------------

In WebGL objects are built using sets of vertices, each of which has a position and a color. By default, all other pixels' colors (and all its other attributes, including position) are computed using interpolation, automatically creating smooth gradients. Previously, our vertex shader didn't apply any specific colors to the vertices. Between this and the fragment shader assigning the fixed color of white to each pixel, the entire square was rendered as solid white.

Let's say we want to render a gradient in which each corner of the square is a different color: red, blue, green, and white. The first thing to do is to establish these colors for the four vertices. To do this, we first need to create an array of vertex colors, then store it into a WebGL buffer.

**Note:** Add the following function to your `init-buffers.js` module:

```js
function initColorBuffer(gl) {
  const colors = [
    1.0,
    1.0,
    1.0,
    1.0, // white
    1.0,
    0.0,
    0.0,
    1.0, // red
    0.0,
    1.0,
    0.0,
    1.0, // green
    0.0,
    0.0,
    1.0,
    1.0, // blue
  ];

  const colorBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY\_BUFFER, colorBuffer);
  gl.bufferData(gl.ARRAY\_BUFFER, new Float32Array(colors), gl.STATIC\_DRAW);

  return colorBuffer;
}
```

This code starts by creating a JavaScript array containing four 4-value vectors, one for each vertex color. Then a new WebGL buffer is allocated to store these colors, and the array is converted into floats and stored into the buffer.

Of course, we also need to call this new function from `initBuffers()`, and return the new buffer it creates.

**Note:** At the end of your `initBuffers()` function, add the following code, replacing the existing `return` statement:

```js
const colorBuffer = initColorBuffer(gl);

return {
  position: positionBuffer,
  color: colorBuffer,
};
```

To use these colors, the vertex shader needs to be updated to pull the appropriate color from the color buffer.

**Note:** Update the `vsSource` declaration in your `main()` function like this:

```js
// Vertex shader program

const vsSource = `
 attribute vec4 aVertexPosition;
 attribute vec4 aVertexColor;

 uniform mat4 uModelViewMatrix;
 uniform mat4 uProjectionMatrix;

 varying lowp vec4 vColor;

 void main(void) {
 gl\_Position = uProjectionMatrix \* uModelViewMatrix \* aVertexPosition;
 vColor = aVertexColor;
 }
 `;
```

The key difference here is that for each vertex, we pass its color using a `varying` to the fragment shader.

Coloring the fragments
----------------------

In order to pick up the interpolated color for each pixel, we need to change the fragment shader to fetch the value from the `vColor` varying.

**Note:** Update the `fsSource` declaration in your `main()` function like this:

```js
// Fragment shader program

const fsSource = `
 varying lowp vec4 vColor;

 void main(void) {
 gl\_FragColor = vColor;
 }
 `;
```

Each fragment receives the interpolated color based on its position relative to the vertex positions instead of a fixed value.

Drawing using the colors
------------------------

Next, you need to add code to look up the attribute location for the colors and set up that attribute for the shader program.

**Note:** Update the `programInfo` declaration in your `main()` function like this:

```js
// Collect all the info needed to use the shader program.
// Look up which attributes our shader program is using
// for aVertexPosition, aVertexColor and also
// look up uniform locations.
const programInfo = {
  program: shaderProgram,
  attribLocations: {
    vertexPosition: gl.getAttribLocation(shaderProgram, "aVertexPosition"),
    vertexColor: gl.getAttribLocation(shaderProgram, "aVertexColor"),
  },
  uniformLocations: {
    projectionMatrix: gl.getUniformLocation(shaderProgram, "uProjectionMatrix"),
    modelViewMatrix: gl.getUniformLocation(shaderProgram, "uModelViewMatrix"),
  },
};
```

Next, `drawScene()` needs to use these colors when drawing the square.

**Note:** Add the following function to your `draw-scene.js` module:

```js
// Tell WebGL how to pull out the colors from the color buffer
// into the vertexColor attribute.
function setColorAttribute(gl, buffers, programInfo) {
  const numComponents = 4;
  const type = gl.FLOAT;
  const normalize = false;
  const stride = 0;
  const offset = 0;
  gl.bindBuffer(gl.ARRAY\_BUFFER, buffers.color);
  gl.vertexAttribPointer(
    programInfo.attribLocations.vertexColor,
    numComponents,
    type,
    normalize,
    stride,
    offset,
  );
  gl.enableVertexAttribArray(programInfo.attribLocations.vertexColor);
}
```

**Note:** Call the `setColorAttribute()` function from `drawScene()`, right before the `gl.useProgram()` call:

```js
setColorAttribute(gl, buffers, programInfo);
```

The result should now look like this:

View the complete code | Open this demo on a new page

* « Previous
* Next »


Lighting in WebGL - Web APIs

Lighting in WebGL
=================

* « Previous
* Next »

As should be clear by now, WebGL doesn't have much built-in knowledge. It just runs two functions you supply — a vertex shader and a fragment shader — and expects you to write creative functions to get the results you want. In other words, if you want lighting you have to calculate it yourself. Fortunately, it's not all that hard to do, and this article will cover some of the basics.

Simulating lighting and shading in 3D
-------------------------------------

Although going into detail about the theory behind simulated lighting in 3D graphics is far beyond the scope of this article, it's helpful to know a bit about how it works. Instead of discussing it in depth here, take a look at the article on Phong shading at Wikipedia, which provides a good overview of the most commonly used lighting model or if you'd like to see a WebGL based explanation see this article.

There are three basic types of lighting:

**Ambient light** is the light that permeates the scene; it's non-directional and affects every face in the scene equally, regardless of which direction it's facing.

**Directional light** is light that is emitted from a specific direction. This is light that's coming from so far away that every photon is moving parallel to every other photon. Sunlight, for example, is considered directional light.

**Point light** is light that is being emitted from a point, radiating in all directions. This is how many real-world light sources usually work. A light bulb emits light in all directions, for example.

For our purposes, we're going to simplify the lighting model by only considering simple directional and ambient lighting; we won't have any specular highlights or point light sources in this scene. Instead, we'll have our ambient lighting plus a single directional light source, aimed at the rotating cube from the previous demo.

Once you drop out the concept of point sources and specular lighting, there are two pieces of information we'll need in order to implement our directional lighting:

1. We need to associate a **surface normal** with each vertex. This is a vector that's perpendicular to the face at that vertex.
2. We need to know the direction in which the light is traveling; this is defined by the **direction vector**.

Then we update the vertex shader to adjust the color of each vertex, taking into account the ambient lighting as well as the effect of the directional lighting given the angle at which it's striking the face. We'll see how to do that when we look at the code for the shader.

Building the normals for the vertices
-------------------------------------

The first thing we need to do is generate the array of normals for all the vertices that comprise our cube. Since a cube is a very simple object, this is easy to do; obviously for more complex objects, calculating the normals will be more involved.

**Note:** Add this function to your "init-buffer.js" module:

```js
function initNormalBuffer(gl) {
  const normalBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY\_BUFFER, normalBuffer);

  const vertexNormals = [
    // Front
    0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0,

    // Back
    0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0,

    // Top
    0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0,

    // Bottom
    0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0,

    // Right
    1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0,

    // Left
    -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0,
  ];

  gl.bufferData(
    gl.ARRAY\_BUFFER,
    new Float32Array(vertexNormals),
    gl.STATIC\_DRAW,
  );

  return normalBuffer;
}
```

This should look pretty familiar by now; we create a new buffer, bind it to be the buffer we're working with, then send along our array of vertex normals into the buffer by calling `bufferData()`.

As before, we have updated `initBuffers()` to call our new function, and to return the buffer it created.

**Note:** At the end of your `initBuffers()` function, add the following code, replacing the existing `return` statement:

```js
const normalBuffer = initNormalBuffer(gl);

return {
  position: positionBuffer,
  normal: normalBuffer,
  textureCoord: textureCoordBuffer,
  indices: indexBuffer,
};
```

Then we add the code to the "draw-scene.js" module to bind the normals array to a shader attribute so the shader code can get access to it.

**Note:** Add this function to your "draw-scene.js" module:

```js
// Tell WebGL how to pull out the normals from
// the normal buffer into the vertexNormal attribute.
function setNormalAttribute(gl, buffers, programInfo) {
  const numComponents = 3;
  const type = gl.FLOAT;
  const normalize = false;
  const stride = 0;
  const offset = 0;
  gl.bindBuffer(gl.ARRAY\_BUFFER, buffers.normal);
  gl.vertexAttribPointer(
    programInfo.attribLocations.vertexNormal,
    numComponents,
    type,
    normalize,
    stride,
    offset,
  );
  gl.enableVertexAttribArray(programInfo.attribLocations.vertexNormal);
}
```

**Note:** Add this line to the `drawScene()` function of your "draw-scene.js" module, just before the `gl.useProgram()` line:

```js
setNormalAttribute(gl, buffers, programInfo);
```

Finally, we need to update the code that builds the uniform matrices to generate and deliver to the shader a **normal matrix**, which is used to transform the normals when dealing with the current orientation of the cube in relation to the light source.

**Note:** Add the following code to the `drawScene()` function of your "draw-scene.js" module, just after the three `mat4.rotate()` calls:

```js
const normalMatrix = mat4.create();
mat4.invert(normalMatrix, modelViewMatrix);
mat4.transpose(normalMatrix, normalMatrix);
```

**Note:** Add the following code to the `drawScene()` function of your "draw-scene.js" module, just after the two previous `gl.uniformMatrix4fv()` calls:

```js
gl.uniformMatrix4fv(
  programInfo.uniformLocations.normalMatrix,
  false,
  normalMatrix,
);
```

Update the shaders
------------------

Now that all the data the shaders need is available to them, we need to update the code in the shaders themselves.

### The vertex shader

The first thing to do is update the vertex shader so it generates a shading value for each vertex based on the ambient lighting as well as the directional lighting.

**Note:** Update the `vsSource` declaration in your `main()` function like this:

```js
const vsSource = `
 attribute vec4 aVertexPosition;
 attribute vec3 aVertexNormal;
 attribute vec2 aTextureCoord;

 uniform mat4 uNormalMatrix;
 uniform mat4 uModelViewMatrix;
 uniform mat4 uProjectionMatrix;

 varying highp vec2 vTextureCoord;
 varying highp vec3 vLighting;

 void main(void) {
 gl\_Position = uProjectionMatrix \* uModelViewMatrix \* aVertexPosition;
 vTextureCoord = aTextureCoord;

 // Apply lighting effect

 highp vec3 ambientLight = vec3(0.3, 0.3, 0.3);
 highp vec3 directionalLightColor = vec3(1, 1, 1);
 highp vec3 directionalVector = normalize(vec3(0.85, 0.8, 0.75));

 highp vec4 transformedNormal = uNormalMatrix \* vec4(aVertexNormal, 1.0);

 highp float directional = max(dot(transformedNormal.xyz, directionalVector), 0.0);
 vLighting = ambientLight + (directionalLightColor \* directional);
 }
 `;
```

Once the position of the vertex is computed, and we pass the coordinates of the texel corresponding to the vertex to the fragment shader, we can work on computing the shading for the vertex.

The first thing we do is transform the normal based on the current orientation of the cube, by multiplying the vertex's normal by the normal matrix. We can then compute the amount of directional lighting that needs to be applied to the vertex by calculating the dot product of the transformed normal and the directional vector (that is, the direction from which the light is coming). If this value is less than zero, then we pin the value to zero, since you can't have less than zero light.

Once the amount of directional lighting is computed, we can generate the lighting value by taking the ambient light and adding in the product of the directional light's color and the amount of directional lighting to provide. As a result, we now have an RGB value that will be used by the fragment shader to adjust the color of each pixel we render.

### The fragment shader

The fragment shader now needs to be updated to take into account the lighting value computed by the vertex shader.

**Note:** Update the `fsSource` declaration in your `main()` function like this:

```js
const fsSource = `
 varying highp vec2 vTextureCoord;
 varying highp vec3 vLighting;

 uniform sampler2D uSampler;

 void main(void) {
 highp vec4 texelColor = texture2D(uSampler, vTextureCoord);

 gl\_FragColor = vec4(texelColor.rgb \* vLighting, texelColor.a);
 }
 `;
```

Here we fetch the color of the texel, just like we did in the previous example, but before setting the color of the fragment, we multiply the texel's color by the lighting value to adjust the texel's color to take into account the effect of our light sources.

The only thing left is to look up the location of the `aVertexNormal` attribute and the `uNormalMatrix` uniform.

**Note:** Update the `programInfo` declaration in your `main()` function like this:

```js
const programInfo = {
  program: shaderProgram,
  attribLocations: {
    vertexPosition: gl.getAttribLocation(shaderProgram, "aVertexPosition"),
    vertexNormal: gl.getAttribLocation(shaderProgram, "aVertexNormal"),
    textureCoord: gl.getAttribLocation(shaderProgram, "aTextureCoord"),
  },
  uniformLocations: {
    projectionMatrix: gl.getUniformLocation(shaderProgram, "uProjectionMatrix"),
    modelViewMatrix: gl.getUniformLocation(shaderProgram, "uModelViewMatrix"),
    normalMatrix: gl.getUniformLocation(shaderProgram, "uNormalMatrix"),
    uSampler: gl.getUniformLocation(shaderProgram, "uSampler"),
  },
};
```

And that's it!

View the complete code | Open this demo on a new page

Exercises for the reader
------------------------

Obviously, this is a simple example, implementing basic per-vertex lighting. For more advanced graphics, you'll want to implement per-pixel lighting, but this will get you headed in the right direction.

You might also try experimenting with the direction of the light source, the colors of the light sources, and so forth.

* « Previous
* Next »


Animating objects with WebGL - Web APIs

Animating objects with WebGL
============================

* « Previous
* Next »
Making the square rotate
------------------------

In this example, we'll actually rotate our camera. By doing so, it will look as if we are rotating the square. First we'll need some variables in which to track the current rotation of the camera.

**Note:** Add this code at the start of your "webgl-demo.js" script:

```js
let squareRotation = 0.0;
let deltaTime = 0;
```

Now we need to update the `drawScene()` function to apply the current rotation to the camera when drawing it. After translating the camera to the initial drawing position for the square, we apply the rotation.

**Note:** In your "draw-scene.js" module, update the declaration of your `drawScene()` function so it can be passed the rotation to use:

```js
function drawScene(gl, programInfo, buffers, squareRotation) {
```

**Note:** In your `drawScene()` function, right after the line `mat4.translate()` call, add this code:

```js
mat4.rotate(
  modelViewMatrix, // destination matrix
  modelViewMatrix, // matrix to rotate
  squareRotation, // amount to rotate in radians
  [0, 0, 1],
); // axis to rotate around
```

This rotates the modelViewMatrix by the current value of `squareRotation`, around the Z axis.

To actually animate, we need to add code that changes the value of `squareRotation` over time.

**Note:** Add this code at the end of your `main()` function, replacing the existing `drawScene()` call:

```js
let then = 0;

// Draw the scene repeatedly
function render(now) {
  now \*= 0.001; // convert to seconds
  deltaTime = now - then;
  then = now;

  drawScene(gl, programInfo, buffers, squareRotation);
  squareRotation += deltaTime;

  requestAnimationFrame(render);
}
requestAnimationFrame(render);
```

This code uses `requestAnimationFrame` to ask the browser to call the function "`render`" on each frame. `requestAnimationFrame` passes us the time in milliseconds since the page loaded. We convert that to seconds and then subtract from it the last time to compute `deltaTime`, which is the number of second since the last frame was rendered.

Finally, we update `squareRotation`.

View the complete code | Open this demo on a new page

* « Previous
* Next »


Creating 3D objects using WebGL - Web APIs

Creating 3D objects using WebGL
===============================

* « Previous
* Next »

Let's take our square plane into three dimensions by adding five more faces to create a cube. To do this efficiently, we're going to switch from drawing using the vertices directly by calling the `gl.drawArrays()` method to using the vertex array as a table, and referencing individual vertices in that table to define the positions of each face's vertices, by calling `gl.drawElements()`.

Consider: each face requires four vertices to define it, but each vertex is shared by three faces. We can pass a lot fewer data around by building an array of all 24 vertices, then referring to each vertex by its index into that array instead of moving entire sets of coordinates around. If you wonder why we need 24 vertices, and not just 8, it is because each corner belongs to three faces of different colors, and a single vertex needs to have a single specific color; therefore we will create three copies of each vertex in three different colors, one for each face.

Define the positions of the cube's vertices
-------------------------------------------

First, let's build the cube's vertex position buffer by updating the code in `initBuffers()`. This is pretty much the same as it was for the square plane, but somewhat longer since there are 24 vertices (4 per side).

**Note:** In the `initPositionBuffer()` function of your "init-buffers.js" module, replace the `positions` declaration with this code:

```js
const positions = [
  // Front face
  -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0,

  // Back face
  -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0,

  // Top face
  -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0,

  // Bottom face
  -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0,

  // Right face
  1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0,

  // Left face
  -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0,
];
```

Since we've added a z-component to our vertices, we need to update the `numComponents` of our `vertexPosition` attribute to 3.

**Note:** In the `setPositionAttribute()` function of your "draw-scene.js" module, change the `numComponents` constant from `2` to `3`:

```js
const numComponents = 3;
```

Define the vertices' colors
---------------------------

We also need to build an array of colors for each of the 24 vertices. This code starts by defining a color for each face, then uses a loop to assemble an array of all the colors for each of the vertices.

**Note:** In the `initColorBuffer()` function of your "init-buffers.js" module, replace the `colors` declaration with this code:

```js
const faceColors = [
  [1.0, 1.0, 1.0, 1.0], // Front face: white
  [1.0, 0.0, 0.0, 1.0], // Back face: red
  [0.0, 1.0, 0.0, 1.0], // Top face: green
  [0.0, 0.0, 1.0, 1.0], // Bottom face: blue
  [1.0, 1.0, 0.0, 1.0], // Right face: yellow
  [1.0, 0.0, 1.0, 1.0], // Left face: purple
];

// Convert the array of colors into a table for all the vertices.

var colors = [];

for (var j = 0; j < faceColors.length; ++j) {
  const c = faceColors[j];
  // Repeat each color four times for the four vertices of the face
  colors = colors.concat(c, c, c, c);
}
```

Define the element array
------------------------

Once the vertex arrays are generated, we need to build the element array.

**Note:** In your "init-buffer.js" module, add the following function:

```js
function initIndexBuffer(gl) {
  const indexBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ELEMENT\_ARRAY\_BUFFER, indexBuffer);

  // This array defines each face as two triangles, using the
  // indices into the vertex array to specify each triangle's
  // position.

  const indices = [
    0,
    1,
    2,
    0,
    2,
    3, // front
    4,
    5,
    6,
    4,
    6,
    7, // back
    8,
    9,
    10,
    8,
    10,
    11, // top
    12,
    13,
    14,
    12,
    14,
    15, // bottom
    16,
    17,
    18,
    16,
    18,
    19, // right
    20,
    21,
    22,
    20,
    22,
    23, // left
  ];

  // Now send the element array to GL

  gl.bufferData(
    gl.ELEMENT\_ARRAY\_BUFFER,
    new Uint16Array(indices),
    gl.STATIC\_DRAW,
  );

  return indexBuffer;
}
```

The `indices` array defines each face like a pair of triangles, specifying each triangle's vertices as an index into the cube's vertex arrays. Thus the cube is described as a collection of 12 triangles.

Next, you need to call this new function from `initBuffers()`, and return the buffer it creates.

**Note:** At the end of the `initBuffers()` function of your "init-buffers.js" module, add the following code, replacing the existing `return` statement:

```js
const indexBuffer = initIndexBuffer(gl);

return {
  position: positionBuffer,
  color: colorBuffer,
  indices: indexBuffer,
};
```

Drawing the cube
----------------

Next we need to add code to our `drawScene()` function to draw using the cube's index buffer, adding new `gl.bindBuffer()` and `gl.drawElements()` calls.

**Note:** In your `drawScene()` function, add the following code just before the `gl.useProgram` line:

```js
// Tell WebGL which indices to use to index the vertices
gl.bindBuffer(gl.ELEMENT\_ARRAY\_BUFFER, buffers.indices);
```

**Note:** In the `drawScene()` function of your "draw-scene.js" module, replace the block just after the two `gl.uniformMatrix4fv` calls, that contains the `gl.drawArrays()` line, with the following block:

```js
{
  const vertexCount = 36;
  const type = gl.UNSIGNED\_SHORT;
  const offset = 0;
  gl.drawElements(gl.TRIANGLES, vertexCount, type, offset);
}
```

Since each face of our cube is comprised of two triangles, there are 6 vertices per side, or 36 total vertices in the cube, even though many of them are duplicates.

Finally, let's replace our variable `squareRotation` by `cubeRotation` and add a second rotation around the x axis.

**Note:** At the start of your "webgl-demo.js" file, replace the `squareRotation` declaration with this line:

```js
let cubeRotation = 0.0;
```

**Note:** In your `drawScene()` function declaration, replace the `squareRotation` with `cubeRotation`:

```js
function drawScene(gl, programInfo, buffers, cubeRotation) {
```

**Note:** In your `drawScene()` function, replace the `mat4.rotate` call with the following code:

```js
mat4.rotate(
  modelViewMatrix, // destination matrix
  modelViewMatrix, // matrix to rotate
  cubeRotation, // amount to rotate in radians
  [0, 0, 1],
); // axis to rotate around (Z)
mat4.rotate(
  modelViewMatrix, // destination matrix
  modelViewMatrix, // matrix to rotate
  cubeRotation \* 0.7, // amount to rotate in radians
  [0, 1, 0],
); // axis to rotate around (Y)
mat4.rotate(
  modelViewMatrix, // destination matrix
  modelViewMatrix, // matrix to rotate
  cubeRotation \* 0.3, // amount to rotate in radians
  [1, 0, 0],
); // axis to rotate around (X)
```

**Note:** In your `main()` function, replace the code that calls `drawScene()` and updates `squareRotation` to pass in and update `cubeRotation` instead:

```js
drawScene(gl, programInfo, buffers, cubeRotation);
cubeRotation += deltaTime;
```

At this point, we now have an animated cube rotating, its six faces rather vividly colored.

View the complete code | Open this demo on a new page

* « Previous
* Next »


Using textures in WebGL - Web APIs

Using textures in WebGL
=======================

* « Previous
* Next »

Now that our sample program has a rotating 3D cube, let's map a texture onto it instead of having its faces be solid colors.

Loading textures
----------------

The first thing to do is add code to load the textures. In our case, we'll be using a single texture, mapped onto all six sides of our rotating cube, but the same technique can be used for any number of textures.

**Note:** It's important to note that the loading of textures follows cross-domain rules; that is, you can only load textures from sites for which your content has CORS approval. See Cross-domain textures below for details.

**Note:** Add these two functions to your "webgl-demo.js" script:

```js
//
// Initialize a texture and load an image.
// When the image finished loading copy it into the texture.
//
function loadTexture(gl, url) {
  const texture = gl.createTexture();
  gl.bindTexture(gl.TEXTURE\_2D, texture);

  // Because images have to be downloaded over the internet
  // they might take a moment until they are ready.
  // Until then put a single pixel in the texture so we can
  // use it immediately. When the image has finished downloading
  // we'll update the texture with the contents of the image.
  const level = 0;
  const internalFormat = gl.RGBA;
  const width = 1;
  const height = 1;
  const border = 0;
  const srcFormat = gl.RGBA;
  const srcType = gl.UNSIGNED\_BYTE;
  const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue
  gl.texImage2D(
    gl.TEXTURE\_2D,
    level,
    internalFormat,
    width,
    height,
    border,
    srcFormat,
    srcType,
    pixel,
  );

  const image = new Image();
  image.onload = () => {
    gl.bindTexture(gl.TEXTURE\_2D, texture);
    gl.texImage2D(
      gl.TEXTURE\_2D,
      level,
      internalFormat,
      srcFormat,
      srcType,
      image,
    );

    // WebGL1 has different requirements for power of 2 images
    // vs. non power of 2 images so check if the image is a
    // power of 2 in both dimensions.
    if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
      // Yes, it's a power of 2. Generate mips.
      gl.generateMipmap(gl.TEXTURE\_2D);
    } else {
      // No, it's not a power of 2. Turn off mips and set
      // wrapping to clamp to edge
      gl.texParameteri(gl.TEXTURE\_2D, gl.TEXTURE\_WRAP\_S, gl.CLAMP\_TO\_EDGE);
      gl.texParameteri(gl.TEXTURE\_2D, gl.TEXTURE\_WRAP\_T, gl.CLAMP\_TO\_EDGE);
      gl.texParameteri(gl.TEXTURE\_2D, gl.TEXTURE\_MIN\_FILTER, gl.LINEAR);
    }
  };
  image.src = url;

  return texture;
}

function isPowerOf2(value) {
  return (value & (value - 1)) === 0;
}
```

The `loadTexture()` routine starts by creating a WebGL texture object `texture` by calling the WebGL `createTexture()` function. It then uploads a single blue pixel using `texImage2D()`. This makes the texture immediately usable as a solid blue color even though it may take a few moments for our image to download.

To load the texture from the image file, it then creates an `Image` object and assigns the `src` to the URL for our image we wish to use as our texture. The function we assign to `image.onload` will be called once the image has finished downloading. At that point we again call `texImage2D()` this time using the image as the source for the texture. After that we set up filtering and wrapping for the texture based on whether or not the image we download was a power of 2 in both dimensions or not.

WebGL1 can only use non power of 2 textures with filtering set to `NEAREST` or `LINEAR` and it can not generate a mipmap for them. Their wrapping mode must also be set to `CLAMP_TO_EDGE`. On the other hand if the texture is a power of 2 in both dimensions then WebGL can do higher quality filtering, it can use mipmap, and it can set the wrapping mode to `REPEAT` or `MIRRORED_REPEAT`.

An example of a repeated texture is tiling an image of a few bricks to cover a brick wall.

Mipmapping and UV repeating can be disabled with `texParameteri()`. This will allow non-power-of-two (NPOT) textures at the expense of mipmapping, UV wrapping, UV tiling, and your control over how the device will handle your texture.

```js
// gl.NEAREST is also allowed, instead of gl.LINEAR, as neither mipmap.
gl.texParameteri(gl.TEXTURE\_2D, gl.TEXTURE\_MIN\_FILTER, gl.LINEAR);
// Prevents s-coordinate wrapping (repeating).
gl.texParameteri(gl.TEXTURE\_2D, gl.TEXTURE\_WRAP\_S, gl.CLAMP\_TO\_EDGE);
// Prevents t-coordinate wrapping (repeating).
gl.texParameteri(gl.TEXTURE\_2D, gl.TEXTURE\_WRAP\_T, gl.CLAMP\_TO\_EDGE);
```

Again, with these parameters, compatible WebGL devices will automatically accept any resolution for that texture (up to their maximum dimensions). Without performing the above configuration, WebGL requires all samples of NPOT textures to fail by returning transparent black: `rgb(0 0 0 / 0%)`.

To load the image, add a call to our `loadTexture()` function within our `main()` function. This can be added after the `initBuffers(gl)` call.

But also note: Browsers copy pixels from the loaded image in top-to-bottom order — from the top-left corner; but WebGL wants the pixels in bottom-to-top order — starting from the bottom-left corner. (For more details, see Why is my WebGL texture upside-down?.)

So in order to prevent the resulting image texture from having the wrong orientation when rendered, we also need call `pixelStorei()` with the `gl.UNPACK_FLIP_Y_WEBGL` parameter set to `true` — to cause the pixels to be flipped into the bottom-to-top order that WebGL expects.

**Note:** Add the following code to your `main()` function, right after the call to `initBuffers()`:

```js
// Load texture
const texture = loadTexture(gl, "cubetexture.png");
// Flip image pixels into the bottom-to-top order that WebGL expects.
gl.pixelStorei(gl.UNPACK\_FLIP\_Y\_WEBGL, true);
```

**Note:** Finally, download the cubetexture.png file to the same local directory as your JavaScript files.

Mapping the texture onto the faces
----------------------------------

At this point, the texture is loaded and ready to use. But before we can use it, we need to establish the mapping of the texture coordinates to the vertices of the faces of our cube. This replaces all the previously existing code for configuring colors for each of the cube's faces in `initBuffers()`.

**Note:** Add this function to your "init-buffer.js" module:

```js
function initTextureBuffer(gl) {
  const textureCoordBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY\_BUFFER, textureCoordBuffer);

  const textureCoordinates = [
    // Front
    0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
    // Back
    0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
    // Top
    0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
    // Bottom
    0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
    // Right
    0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
    // Left
    0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
  ];

  gl.bufferData(
    gl.ARRAY\_BUFFER,
    new Float32Array(textureCoordinates),
    gl.STATIC\_DRAW,
  );

  return textureCoordBuffer;
}
```

First, this code creates a WebGL buffer into which we'll store the texture coordinates for each face, then we bind that buffer as the array we'll be writing into.

The `textureCoordinates` array defines the texture coordinates corresponding to each vertex of each face. Note that the texture coordinates range from 0.0 to 1.0; the dimensions of textures are normalized to a range of 0.0 to 1.0 regardless of their actual size, for the purpose of texture mapping.

Once we've set up the texture mapping array, we pass the array into the buffer, so that WebGL has that data ready for its use.

Then we return the new buffer.

Next, we need to update `initBuffers()` to create and return the texture coordinates buffer instead of the color buffer.

**Note:** In the `initBuffers()` function of your "init-buffers.js" module, replace the call to `initColorBuffer()` with the following line:

```js
const textureCoordBuffer = initTextureBuffer(gl);
```

**Note:** In the `initBuffers()` function of your "init-buffers.js" module, replace the `return` statement with the following:

```js
return {
  position: positionBuffer,
  textureCoord: textureCoordBuffer,
  indices: indexBuffer,
};
```

Updating the shaders
--------------------

The shader program also needs to be updated to use the textures instead of solid colors.

### The vertex shader

We need to replace the vertex shader so that instead of fetching color data, it instead fetches the texture coordinate data.

**Note:** Update the `vsSource` declaration in your `main()` function like this:

```js
const vsSource = `
 attribute vec4 aVertexPosition;
 attribute vec2 aTextureCoord;

 uniform mat4 uModelViewMatrix;
 uniform mat4 uProjectionMatrix;

 varying highp vec2 vTextureCoord;

 void main(void) {
 gl\_Position = uProjectionMatrix \* uModelViewMatrix \* aVertexPosition;
 vTextureCoord = aTextureCoord;
 }
 `;
```

The key change here is that instead of fetching the vertex color, we're fetching the texture coordinates and passing them to the vertex shader; this will indicate the location within the texture corresponding to the vertex.

### The fragment shader

The fragment shader likewise needs to be updated.

**Note:** Update the `fsSource` declaration in your `main()` function like this:

```js
const fsSource = `
 varying highp vec2 vTextureCoord;

 uniform sampler2D uSampler;
 out vec4 fragColor;

 void main(void) {
 fragColor = texture(uSampler, vTextureCoord);
 }
 `;
```

Instead of assigning a color value to the fragment's color, the fragment's color is computed by fetching the texel (that is, the pixel within the texture) based on the value of `vTextureCoord` which like the colors is interpolated between vertices.

### Attribute and Uniform Locations

Because we changed an attribute and added a uniform we need to look up their locations.

**Note:** Update the `programInfo` declaration in your `main()` function like this:

```js
const programInfo = {
  program: shaderProgram,
  attribLocations: {
    vertexPosition: gl.getAttribLocation(shaderProgram, "aVertexPosition"),
    textureCoord: gl.getAttribLocation(shaderProgram, "aTextureCoord"),
  },
  uniformLocations: {
    projectionMatrix: gl.getUniformLocation(shaderProgram, "uProjectionMatrix"),
    modelViewMatrix: gl.getUniformLocation(shaderProgram, "uModelViewMatrix"),
    uSampler: gl.getUniformLocation(shaderProgram, "uSampler"),
  },
};
```

Drawing the textured cube
-------------------------

The changes to the `drawScene()` function are simple.

**Note:** In the `drawScene()` function of your "draw-scene.js" module, add the following function:

```js
// tell webgl how to pull out the texture coordinates from buffer
function setTextureAttribute(gl, buffers, programInfo) {
  const num = 2; // every coordinate composed of 2 values
  const type = gl.FLOAT; // the data in the buffer is 32-bit float
  const normalize = false; // don't normalize
  const stride = 0; // how many bytes to get from one set to the next
  const offset = 0; // how many bytes inside the buffer to start from
  gl.bindBuffer(gl.ARRAY\_BUFFER, buffers.textureCoord);
  gl.vertexAttribPointer(
    programInfo.attribLocations.textureCoord,
    num,
    type,
    normalize,
    stride,
    offset,
  );
  gl.enableVertexAttribArray(programInfo.attribLocations.textureCoord);
}
```

**Note:** In the `drawScene()` function of your "draw-scene.js" module, replace the call to `setColorAttribute()` with the following line:

```js
setTextureAttribute(gl, buffers, programInfo);
```

Then add code to specify the texture to map onto the faces.

**Note:** In your `drawScene()` function, just after the two calls to `gl.uniformMatrix4fv()`, add the following code:

```js
// Tell WebGL we want to affect texture unit 0
gl.activeTexture(gl.TEXTURE0);

// Bind the texture to texture unit 0
gl.bindTexture(gl.TEXTURE\_2D, texture);

// Tell the shader we bound the texture to texture unit 0
gl.uniform1i(programInfo.uniformLocations.uSampler, 0);
```

WebGL provides a minimum of 8 texture units; the first of these is `gl.TEXTURE0`. We tell WebGL we want to affect unit 0. We then call `bindTexture()` which binds the texture to the `TEXTURE_2D` bind point of texture unit 0. We then tell the shader that for the `uSampler` use texture unit 0.

Lastly, add `texture` as a parameter to the `drawScene()` function, both where it is defined and where it is called.

**Note:** Update the declaration of your `drawScene()` function to add the new parameter:

```js
function drawScene(gl, programInfo, buffers, texture, cubeRotation) {
```

**Note:** Update the place in your `main()` function where you call `drawScene()`:

```js
drawScene(gl, programInfo, buffers, texture, cubeRotation);
```

At this point, the rotating cube should be good to go.

View the complete code | Open this demo on a new page

Cross-domain textures
---------------------

Loading of WebGL textures is subject to cross-domain access controls. In order for your content to load a texture from another domain, CORS approval needs to be obtained. See HTTP access control for details on CORS.

Because WebGL now requires textures to be loaded from secure contexts, you can't use textures loaded from `file:///` URLs in WebGL. That means that you'll need a secure web server to test and deploy your code. For local testing, see our guide How do you set up a local testing server? for help.

See this hacks.mozilla.org article for an explanation of how to use CORS-approved images as WebGL textures.

Tainted (write-only) 2D canvases can't be used as WebGL textures. A 2D `<canvas>` becomes tainted, for example, when a cross-domain image is drawn on it.

* « Previous
* Next »


Getting started with WebGL - Web APIs

Getting started with WebGL
==========================

* Next »

WebGL enables web content to use an API based on OpenGL ES 2.0 to perform 2D and 3D rendering in an HTML `canvas` in browsers that support it without the use of plug-ins.

WebGL programs consist of control code written in JavaScript and shader code (GLSL) that is executed on a computer's Graphics Processing Unit (GPU). WebGL elements can be mixed with other HTML elements and composited with other parts of the page or page background.

This article will introduce you to the basics of using WebGL. It's assumed that you already have an understanding of the mathematics involved in 3D graphics, and this article doesn't pretend to try to teach you 3D graphics concepts itself.

The code examples in this tutorial can also be found in the webgl-examples folder on GitHub.

It's worth noting here that this series of articles introduces WebGL itself; however, there are a number of frameworks available that encapsulate WebGL's capabilities, making it easier to build 3D applications and games, such as THREE.js and BABYLON.js.

Preparing to render in 3D
-------------------------

First, create two new files:

* "index.html"
* "webgl-demo.js"

The "index.html" file should contain the following:

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>WebGL Demo</title>
    <script src="webgl-demo.js" type="module"></script>
  </head>

  <body>
    <canvas id="glcanvas" width="640" height="480"></canvas>
  </body>
</html>
```

Note that this declares a canvas that our sample will draw into.

### Preparing the WebGL context

Add the following code to the "webgl-demo.js" file:

```js
main();

//
// start here
//
function main() {
  const canvas = document.querySelector("#glcanvas");
  // Initialize the GL context
  const gl = canvas.getContext("webgl");

  // Only continue if WebGL is available and working
  if (gl === null) {
    alert(
      "Unable to initialize WebGL. Your browser or machine may not support it.",
    );
    return;
  }

  // Set clear color to black, fully opaque
  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  // Clear the color buffer with specified clear color
  gl.clear(gl.COLOR\_BUFFER\_BIT);
}
```

The `main()` function is called when our script is loaded. Its purpose is to set up the WebGL context and start rendering content.

The first thing we do here is obtain a reference to the canvas, assigning it to a variable named `canvas`.

Once we have the canvas, we try to get a `WebGLRenderingContext` for it by calling `getContext()` and passing it the string `"webgl"`. If the browser does not support WebGL, `getContext()` will return `null` in which case we display a message to the user and exit.

If the context is successfully initialized, the variable `gl` is our reference to it. In this case, we set the clear color to black, and clear the context to that color (redrawing the canvas with the background color).

At this point, you have enough code that the WebGL context should successfully initialize, and you should wind up with a big black, empty box, ready and waiting to receive content.

View the complete code | Open this demo on a new page

See also
--------

* An introduction to WebGL: Written by Luz Caballero, published at dev.opera.com. This article addresses what WebGL is, explains how WebGL works (including the rendering pipeline concept), and introduces some WebGL libraries.
* WebGL Fundamentals
* An intro to modern OpenGL: A series of nice articles about OpenGL written by Joe Groff, providing a clear introduction to OpenGL from its history to the important graphics pipeline concept, and also includes some examples to demonstrate how OpenGL works. If you have no idea what OpenGL is, this is a good place to start.
* Next »


AudioContext: outputLatency property - Web APIs

AudioContext: outputLatency property
====================================

 The **`outputLatency`** read-only property of
 the `AudioContext` Interface provides an estimation of the output latency
 of the current audio context.

 This is the time, in seconds, between the browser passing an audio buffer out of an
 audio graph over to the host system's audio subsystem to play, and the time at which the
 first sample in the buffer is actually processed by the audio output device.

It varies depending on the platform and the available hardware.

Value
-----

A double representing the output latency in seconds.

Examples
--------

```js
const audioCtx = new AudioContext();
console.log(audioCtx.outputLatency);
```

Specifications
--------------

| Specification |
| --- |
| Web Audio API # dom-audiocontext-outputlatency |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* Using the Web Audio API
* Web Audio API


AudioContext: resume() method - Web APIs

AudioContext: resume() method
=============================

 The **`resume()`** method of the `AudioContext`
 interface resumes the progression of time in an audio context that has previously been
 suspended.

 This method will cause an `INVALID_STATE_ERR` exception to be thrown if
 called on an `OfflineAudioContext`.

Syntax
------

```js
resume()
```

### Parameters

None.

### Return value

 A `Promise` that resolves when the context has resumed. The promise is
 rejected if the context has already been closed.

Examples
--------

 The following snippet is taken from our AudioContext states demo (see it running live.) When the suspend/resume button is clicked, the
 `AudioContext.state` is queried — if it is `running`,
 `suspend()` is called; if it is
 `suspended`, `resume()` is called. In each case, the text label of
 the button is updated as appropriate once the promise resolves.

```js
susresBtn.onclick = () => {
  if (audioCtx.state === "running") {
    audioCtx.suspend().then(() => {
      susresBtn.textContent = "Resume context";
    });
  } else if (audioCtx.state === "suspended") {
    audioCtx.resume().then(() => {
      susresBtn.textContent = "Suspend context";
    });
  }
};
```

Specifications
--------------

| Specification |
| --- |
| Web Audio API # dom-audiocontext-resume |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* Using the Web Audio API
* Web Audio API


AudioContext: getOutputTimestamp() method - Web APIs

AudioContext: getOutputTimestamp() method
=========================================

 The
 **`getOutputTimestamp()`** method of the
 `AudioContext` interface returns a new `AudioTimestamp` object
 containing two audio timestamp values relating to the current audio context.

The two values are as follows:

* `AudioTimestamp.contextTime`: The time of the sample frame currently
 being rendered by the audio output device (i.e., output audio stream position), in the
 same units and origin as the context's `AudioContext.currentTime`.
 Basically, this is the time after the audio context was first created.
* `AudioTimestamp.performanceTime`: An estimation of the moment when the
 sample frame corresponding to the stored `contextTime` value was rendered
 by the audio output device, in the same units and origin as
 `performance.now()`. This is the time after the document containing the
 audio context was first rendered.
Syntax
------

```js
getOutputTimestamp()
```

### Parameters

None.

### Return value

An `AudioTimestamp` object, which has the following properties.

* `contextTime`: A point in the time coordinate system of the
 `currentTime` for the
 `BaseAudioContext`; the time after the audio context was first created.
* `performanceTime`: A point in the time coordinate system of a
 `Performance` interface; the time after the document containing the audio
 context was first rendered
Examples
--------

 In the following code we start to play an audio file after a play button is clicked,
 and start off a `requestAnimationFrame` loop running, which constantly
 outputs the `contextTime` and `performanceTime`.

You can see full code of this example at output-timestamp (see it live also).

```js
// Press the play button
playBtn.addEventListener("click", () => {
  // We can create the audioCtx as there has been some user action
  if (!audioCtx) {
    audioCtx = new AudioContext();
  }
  source = new AudioBufferSourceNode(audioCtx);
  getData();
  source.start(0);
  playBtn.disabled = true;
  stopBtn.disabled = false;
  rAF = requestAnimationFrame(outputTimestamps);
});

// Press the stop button
stopBtn.addEventListener("click", () => {
  source.stop(0);
  playBtn.disabled = false;
  stopBtn.disabled = true;
  cancelAnimationFrame(rAF);
});

// Helper function to output timestamps
function outputTimestamps() {
  const ts = audioCtx.getOutputTimestamp();
  output.textContent = `Context time: ${ts.contextTime} | Performance time: ${ts.performanceTime}`;
  rAF = requestAnimationFrame(outputTimestamps); // Reregister itself
}
```

Specifications
--------------

| Specification |
| --- |
| Web Audio API # dom-audiocontext-getoutputtimestamp |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


AudioContext: createMediaElementSource() method - Web APIs

AudioContext: createMediaElementSource() method
===============================================

The `createMediaElementSource()` method of the `AudioContext` Interface is used to create a new `MediaElementAudioSourceNode` object, given an existing HTML `<audio>` or `<video>` element, the audio from which can then be played and manipulated.

For more details about media element audio source nodes, check out the `MediaElementAudioSourceNode` reference page.

Syntax
------

```js
createMediaElementSource(myMediaElement)
```

### Parameters

`myMediaElement`

An `HTMLMediaElement` object that you want to feed into an audio processing graph to manipulate.

### Return value

A `MediaElementAudioSourceNode`.

Examples
--------

This simple example creates a source from an `<audio>` element using `createMediaElementSource()`, then passes the audio through a `GainNode` before feeding it into the `AudioDestinationNode` for playback. When the mouse pointer is moved, the `updatePage()` function is invoked, which calculates the current gain as a ratio of mouse Y position divided by overall window height. You can therefore increase and decrease the volume of the playing music by moving the mouse pointer up and down.

**Note:** You can also view this example running live, or view the source.

```js
const audioCtx = new AudioContext();
const myAudio = document.querySelector("audio");

// Create a MediaElementAudioSourceNode
// Feed the HTMLMediaElement into it
const source = audioCtx.createMediaElementSource(myAudio);

// Create a gain node
const gainNode = audioCtx.createGain();

// Create variables to store mouse pointer Y coordinate
// and HEIGHT of screen
let curY;
const HEIGHT = window.innerHeight;

// Get new mouse pointer coordinates when mouse is moved
// then set new gain value
document.onmousemove = updatePage;

function updatePage(e) {
  curY = e.pageY;
  gainNode.gain.value = curY / HEIGHT;
}

// Connect the AudioBufferSourceNode to the gainNode
// and the gainNode to the destination, so we can play the
// music and adjust the volume using the mouse cursor
source.connect(gainNode);
gainNode.connect(audioCtx.destination);
```

**Note:** As a consequence of calling `createMediaElementSource()`, audio playback from the `HTMLMediaElement` will be re-routed into the processing graph of the AudioContext. So playing/pausing the media can still be done through the media element API and the player controls.

Specifications
--------------

| Specification |
| --- |
| Web Audio API # dom-audiocontext-createmediaelementsource |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* Using the Web Audio API


AudioContext: close() method - Web APIs

AudioContext: close() method
============================

The `close()` method of the `AudioContext` Interface closes the audio context, releasing any system audio resources that it uses.

This function does not automatically release all `AudioContext`-created objects, unless other references have been released as well; however, it will forcibly release any system audio resources that might prevent additional `AudioContexts` from being created and used, suspend the progression of audio time in the audio context, and stop processing audio data. The returned `Promise` resolves when all `AudioContext`-creation-blocking resources have been released. This method throws an `INVALID_STATE_ERR` exception if called on an `OfflineAudioContext`.

Syntax
------

```js
close()
```

### Parameters

None.

### Return value

A `Promise` that resolves with `undefined`.

Examples
--------

The following snippet is taken from our AudioContext states demo (see it running live.) When the stop button is clicked, `close()` is called. When the promise resolves, the example is reset to its beginning state.

```js
stopBtn.onclick = () => {
  audioCtx.close().then(() => {
    startBtn.removeAttribute("disabled");
    susresBtn.setAttribute("disabled", "disabled");
    stopBtn.setAttribute("disabled", "disabled");
  });
};
```

Specifications
--------------

| Specification |
| --- |
| Web Audio API # dom-audiocontext-close |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* Using the Web Audio API
* Web Audio API


AudioContext: sinkchange event - Web APIs

AudioContext: sinkchange event
==============================

**Experimental:** **This is an experimental technology**
Check the Browser compatibility table carefully before using this in production.

The **`sinkchange`** event of the `AudioContext` interface is fired when the output audio device (and therefore, the `AudioContext.sinkId`) has changed.

Syntax
------

Use the event name in methods like `addEventListener()`, or set an event handler property.

```js
addEventListener("sinkchange", (event) => {});

onsinkchange = (event) => {};
```

Event type
----------

`Event`.

Examples
--------

A `sinkchange` event listener can be used to report a change of audio output device. Note that if `sinkId` contains an `AudioSinkInfo` object, it indicates that the audio has been changed to not play on any output device.

```js
audioCtx.addEventListener("sinkchange", () => {
  if (typeof audioCtx.sinkId === "object" && audioCtx.sinkId.type === "none") {
    console.log("Audio changed to not play on any device");
  } else {
    console.log(`Audio output device changed to ${audioCtx.sinkId}`);
  }
});
```

See our SetSinkId test example for working code.

Specifications
--------------

| Specification |
| --- |
| Web Audio API # eventdef-audiocontext-sinkchange |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* SetSinkId test example
* Change the destination output device in Web Audio
* `AudioContext.setSinkId()`
* `AudioContext.sinkId`