XRWebGLLayer: XRWebGLLayer() constructor - Web APIs

XRWebGLLayer: XRWebGLLayer() constructor
========================================

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

 The WebXR Device API **`XRWebGLLayer()`** constructor creates and
 returns a new `XRWebGLLayer` object, providing the linkage between the
 WebXR device and the WebGL graphics layer used to render the 3D scene.

Syntax
------

```js
new XRWebGLLayer(session, context)
new XRWebGLLayer(session, context, options)
```

### Parameters

`session`

 An `XRSession` object specifying the WebXR session which will be
 rendered using the WebGL context.

`context`

 A `WebGLRenderingContext` or `WebGL2RenderingContext`
 identifying the WebGL drawing context to use for rendering the scene for the specified
 WebXR session.

`options` Optional

 An object providing configuration options for the new `XRWebGLLayer`. The available options
 are:

`alpha`

The frame buffer's color buffer will be established with an alpha channel if the `alpha` Boolean property is `true`. Otherwise, the color buffer will not have an alpha channel. The default value is `true`.

`antialias`

A Boolean value which is `true` if anti-aliasing is to be used when rendering in the context; otherwise `false`. The browser selects the anti-aliasing method to use; there is no support for requesting a specific mode yet. The default value is `true`.

`depth`

A Boolean value which, if `true`, requests that the new layer have a depth buffer; otherwise, no depth layer is allocated. The default is `true`.

`framebufferScaleFactor`

A floating-point value which is used to scale the image during compositing, with a value of 1.0 represents the default pixel size for the frame buffer. The static `XRWebGLLayer` function `XRWebGLLayer.getNativeFramebufferScaleFactor()` returns the scale that would result in a 1:1 pixel ratio, thereby ensuring that the rendering is occurring at the device's native resolution. The default is 1.0.

`ignoreDepthValues`

A Boolean value which indicates whether or not to ignore the contents of the depth buffer while compositing the scene. The default is `false`.

`stencil`

A Boolean value which, if `true`, requests that the new layer include a stencil buffer. Otherwise, no stencil buffer is allocated. The default is `false`.

### Return value

 A newly-created `XRWebGLLayer` which links the specified
 `XRSession` to the WebGL context given by `context`, which will
 be used as the renderer for the session. Any options specified in `layerInit`
 are used to tailor the rendering system's configuration.

### Exceptions

`InvalidStateError` `DOMException`

 Thrown if the new `XRWebGLLayer` could not be created due to one of a number of
 possible state errors:

* The `XRSession` specified by `session` has already been
 stopped.
* The specified WebGL context, `context`, has been lost for any reason, such as a GPU switch or reset.
* The specified `session` is immersive but the `context` is
 not WebXR compatible.

`OperationError` `DOMException`

 Thrown if the resources (including memory buffers) needed for the layer to operate could not
 be allocated.

Examples
--------

 In this example, a new `XRWebGLLayer` is created for a WebXR session,
 `xrSession`.

```js
xrSession.updateRenderState({
  baseLayer: new XRWebGLLayer(xrSession, gl, {
    alpha: false,
    antialias: false,
    depth: false,
    framebufferScaleFactor: 0.5,
    ignoreDepthValues: true,
    stencil: false,
  }),
});
```

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

| Specification |
| --- |
| WebXR Device API # dom-xrwebgllayer-xrwebgllayer |

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

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

See also
--------

* WebXR Device API
* Getting started with WebGL
* Handling lost context in WebGL: Khronos WebGL wiki


XRWebGLLayer: antialias property - Web APIs

XRWebGLLayer: antialias property
================================

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

 The read-only `XRWebGLLayer` property
 **`antialias`** is a Boolean value which is `true`
 if the rendering layer's frame buffer supports anti-aliasing. Otherwise, this
 property's value is `false`. The specific anti-aliasing technique used is left
 to the user agent's discretion and cannot be specified by
 the website or web app.

Syntax
------

```js
xrWebGLLayer.antialias
```

### Value

 A Boolean value which is `true` if the WebGL rendering layer's frame buffer
 is configured to support anti-aliasing. Otherwise, this property is `false`.

 When the WebXR compositor is enabled, this value corresponds to the value of the
 `antialias` property on the object returned by the WebGL context's
 `getContentAttributes()`
 method.

Usage notes
-----------

 Since this is a read-only property, you can set the anti-aliasing mode only when
 initially creating the `XRWebGLLayer`, by specifying the `antialias`
 property in the `XRWebGLLayer()`
 constructor's `options` configuration object.

Examples
--------

 This snippet checks the value of `antialias` to see if it should perform
 additional work to attempt to compensate for the lack of anti-aliasing on the WebGL
 layer.

```js
let glLayer = xrSession.renderState.baseLayer;
gl.bindFrameBuffer(gl.FRAMEBUFFER, glLayer.framebuffer);

/\* .. \*/

if (!glLayer.antialias) {
  /\* compensate for lack of anti-aliasing \*/
}
```

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

| Specification |
| --- |
| WebXR Device API # dom-xrwebgllayer-antialias |

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

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

See also
--------

* WebXR Device API


XRWebGLLayer: framebufferWidth property - Web APIs

XRWebGLLayer: framebufferWidth property
=======================================

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

 The read-only `XRWebGLLayer` property
 **`framebufferWidth`** specifies the width of the framebuffer,
 in pixels.

 You can get the height of the framebuffer using the
 `framebufferHeight` property.

Value
-----

 The width in pixels of the XR device's framebuffer. Each of the framebuffer's
 attachments (pixel, depth, color, and/or stencil buffers, for example) are all this many
 pixels wide.

Examples
--------

This snippet fetches the framebuffer's width and height for later use.

```js
let glLayer = xrSession.renderState.baseLayer;
gl.bindFramebuffer(gl.FRAMEBUFFER, glLayer.framebuffer);

frameWidth = glLayer.framebufferWidth;
frameHeight = glLayer.framebufferHeight;
```

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

| Specification |
| --- |
| WebXR Device API # dom-xrwebgllayer-framebufferwidth |

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

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

See also
--------

* WebXR Device API


XRWebGLLayer: ignoreDepthValues property - Web APIs

XRWebGLLayer: ignoreDepthValues property
========================================

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

 The read-only `XRWebGLLayer` property
 **`ignoreDepthValues`** is a Boolean value which is
 `true` if the session has been configured to ignore the values in the depth
 buffer while rendering the scene. If the depth buffer is being used to determine the
 position of vertices, this property is `false`.

 The value of `ignoreDepthValues` can only be set when the
 `XRWebGLLayer` is instantiated, by setting the corresponding value in the constructor's `options` parameter.

Value
-----

 A Boolean value which is `true` if the WebGL context's depth buffer is being
 used while computing the locations of points in the 3D world. Otherwise, if this is
 `true`, the depth buffer's values are being used to assist in placing objects
 in the scene. Since the XR compositor uses the depth buffer by default, this value is `false`
 unless explicitly set otherwise when creating the `XRWebGLLayer` using its
 constructor, `XRWebGLLayer()`.

Usage notes
-----------

 When the `ignoreDepthValues` property is `false`, the XR
 compositor uses the values found in the depth buffer, which should be accurate for the
 scene, in order to potentially improve the quality or the output as well as the comfort
 level for the viewer.

 The depth buffer is `framebufferWidth`
 entries wide and `framebuffer` entries
 tall. Each entry in the buffer specifies the depth at which the corresponding pixel is
 located, and has a value between 0.0 and 1.0.

 A depth buffer pixel value of 0.0 corresponds to the depth given by the session's
 `depthNear` and a value of 1.0 corresponds to
 the depth given by `depthFar`.

 The depth, in tandem with the coordinates of each point being rendered, makes it
 possible to more accurately represent the scene in the 3D space.

Examples
--------

 If the Web application which is using WeXR is rendering its content without using a
 depth buffer—or if the depth buffer's contents are invalid—you should disable the use of
 the depth buffer for WebXR rendering by setting `ignoreDepthValues` to true
 when creating the `XRWebGLLayer`. This is demonstrated in the snippet of
 code below:

```js
const glLayerOptions = {
  ignoreDepthValues: true,
};

let glLayer = new XRWebGLLayer(xrSession, gl, glLayerOptions);
```

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

| Specification |
| --- |
| WebXR Device API # dom-xrwebgllayer-ignoredepthvalues |

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

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

See also
--------

* WebXR Device API
* WebGL depth buffer related methods: `depthFunc()`, `clearDepth()`


XRWebGLLayer: getViewport() method - Web APIs

XRWebGLLayer: getViewport() method
==================================

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

 The `XRWebGLLayer` interface's
 **`getViewport()`** method returns the
 `XRViewport` that should be used to render the specified
 `XRView` into the WebGL layer. For WebXR devices which use a
 single framebuffer for both the left and right eyes, the returned viewport represents
 the region of the framebuffer into which the scene should be rendered for the eye
 represented by the view.

Syntax
------

```js
getViewport(view)
```

### Parameters

`view`

 An `XRView` object indicating the view for which the viewport is to be
 returned.

### Return value

 A `XRViewport` object representing the viewport which will restrict
 drawing to the portion of the layer corresponding to the specified `view`.

### Exceptions

`InvalidStateError` `DOMException`

 Thrown if either the specified `view` is not in an active `XRFrame` or
 that `XRFrame` and the `XRWebGLLayer` are not part of the same
 WebXR session.

Examples
--------

 This example demonstrates in part what the callback for the
 `requestAnimationFrame()` function might
 look like, using `getViewport()` to get the viewport so that drawing can be
 constrained to the area set aside for the eye whose viewpoint is currently being
 rendered.

 This works because the set of views returned by an `XRViewerPose` each
 represent one eye's perspective on the scene. Since the framebuffer is split in half,
 one half for each eye, setting the WebGL viewport to match the WebXR layer's viewport
 will ensure that when rendering the scene for the current eye's pose, it is rendered
 into the correct half of the framebuffer.

**<<<--- add link to appropriate section in the Cameras and views
article --->>>**

```js
function drawFrame(time, frame) {
  const session = frame.session;

  const pose = frame.getViewerPose(mainReferenceSpace);

  if (pose) {
    const glLayer = session.renderState.baseLayer;
    gl.bindFramebuffer(gl.FRAMEBUFFER, glLayer.framebuffer);

    gl.clearColor(0, 0, 0, 1.0);
    gl.clearDepth(1.0);
    gl.clear(gl.COLOR\_BUFFER\_BIT, gl.DEPTH\_COLOR\_BIT);

    for (const view of pose.views) {
      const viewport = glLayer.getViewport(view);
      gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);

      /\* Render the scene now \*/
    }
  }
}
```

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

| Specification |
| --- |
| WebXR Device API # dom-xrwebgllayer-getviewport |

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

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

See also
--------

* WebXR Device API


XRWebGLLayer: fixedFoveation property - Web APIs

XRWebGLLayer: fixedFoveation property
=====================================

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

The **`fixedFoveation`** property of the `XRWebGLLayer` interface is a number indicating the amount of foveation used by the XR compositor. Fixed Foveated Rendering (FFR) renders the edges of the eye textures at a lower resolution than the center and reduces the GPU load.

It is most useful for low contrast textures, such as background images but less for high contrast ones such as text or detailed images. Authors can adjust the level on a per frame basis to achieve the best tradeoff between performance and visual quality.

Value
-----

A number between 0 and 1.

* The minium amount of foveation is indicated by 0 (full resolution).
* The maximum amount of foveation is indicated by 1 (the edges render at lower resolution).

It's up to the user agent how to interpret the numbers in this range. When changing the foveation level, the effect will visible in the next `XRFrame`.

Note that some user agents might implement certain levels of foveation, so you might need to adjust the foveation level in large increments to see an effect. Example levels:

* `0`: no foveation
* `1/3`: low foveation
* `2/3`: medium foveation
* `1.0`: maximum foveation

Some devices don't support foveated rendering. In that case `fixedFoveation` is `null` and setting it will not do anything.

Examples
--------

### Dynamically setting the level of fixed foveation rendering

The `fixedFoveation` property allows you to set the level of foveation at runtime and for each frame. To set the maximum foveation for a given `XRWebGLLayer`, use a value of `1`.

```js
let glLayer = xrSession.renderState.baseLayer;
glLayer.fixedFoveation = 1; // maximum foveation
```

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

| Specification |
| --- |
| WebXR Device API # dom-xrwebgllayer-fixedfoveation |

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

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

See also
--------

* Foveated rendering


PresentationConnectionAvailableEvent: connection property - Web APIs

PresentationConnectionAvailableEvent: connection property
=========================================================

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

When an incoming connection is created, a receiving user agent fires a trusted event, named `connectionavailable`, on a `PresentationReceiver`. The trusted event is fired at the presentation controller's monitor, using the `PresentationConnectionAvailableEvent` interface, with the `connection` attribute set to the `PresentationConnection` object that was created.

The event is fired for all connections that are created when monitoring incoming presentation connections.

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

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


PresentationConnectionAvailableEvent: PresentationConnectionAvailableEvent() constructor - Web APIs

PresentationConnectionAvailableEvent: PresentationConnectionAvailableEvent() constructor
========================================================================================

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

The **`PresentationConnectionAvailableEvent()`** constructor creates a new `PresentationConnectionAvailableEvent` object.

Syntax
------

```js
new PresentationConnectionAvailableInit(type, options)
```

### Parameters

`type`

 A string with the name of the event.
 It is case-sensitive and browsers set it to `connectionavailable`.

`options`

An object that, *in addition of the properties defined in `Event()`*, can have the following properties:

`connection`

The association `PresentationConnection` object.

### Return value

A new `PresentationConnectionAvailableEvent` object.

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

| Specification |
| --- |
| Presentation API # dom-presentationconnectionavailableevent-constructor |

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

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


TextDecoderStream: TextDecoderStream() constructor - Web APIs

TextDecoderStream: TextDecoderStream() constructor
==================================================

The **`TextDecoderStream()`** constructor creates a new `TextDecoderStream` object which is used to convert a stream of text in a binary encoding into strings.

Syntax
------

```js
new TextDecoderStream(label)
new TextDecoderStream(label, options)
```

### Parameters

`label`

 A string defaulting to `utf-8`.
 This may be any valid label.

`options` Optional

An object with the following properties:

`fatal` Optional

 A boolean value indicating if the `TextDecoder.decode()` method must throw a `TypeError` when decoding invalid data.
 It defaults to `false`, which means that the decoder will substitute malformed data with a replacement character.

`ignoreBOM` Optional

 A boolean value indicating whether the byte order mark will be included in the output or skipped over.
 It defaults to `false`, which means that the byte order mark will be skipped over when decoding and will not be included in the decoded text.

### Exceptions

`RangeError`

Thrown if the value of `label` is unknown, or is one of the values leading to a `'replacement'` decoding algorithm (`"iso-2022-cn"` or `"iso-2022-cn-ext"`).

Examples
--------

 The following example demonstrates how to decode binary data retrieved from a `fetch()` call.
 The data will be interpreted as UTF-8, as no `label` has been passed.

```js
const response = await fetch("https://example.com");
const stream = response.body.pipeThrough(new TextDecoderStream());
```

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

| Specification |
| --- |
| Encoding Standard # ref-for-dom-textdecoderstream① |

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

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


TextDecoderStream: readable property - Web APIs

TextDecoderStream: readable property
====================================

The **`readable`** read-only property of the `TextDecoderStream` interface returns a `ReadableStream`.

Value
-----

A `ReadableStream`.

Examples
--------

This example shows how to return a `ReadableStream` from a `TextDecoderStream`.

```js
stream = new TextDecoderStream();
console.log(stream.readable); //a ReadableStream
```

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

| Specification |
| --- |
| Streams Standard # dom-generictransformstream-readable |

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

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


TextDecoderStream: fatal property - Web APIs

TextDecoderStream: fatal property
=================================

The **`fatal`** read-only property of the `TextDecoderStream` interface is a `boolean` indicating if the error mode of the `TextDecoderStream` object is set to `fatal`.

 If the property is `true` then a decoder will throw a `TypeError` if it encounters malformed data while decoding.
 If `false` the decoder will substitute the invalid data with the replacement character `U+FFFD` (�).
 The value of the property is set in the `TextDecoderStream()` constructor.

Value
-----

 A `boolean` which will return `true` if the error mode is set to "fatal".
 Otherwise it returns `false`, indicating that the error mode is "replacement".

Examples
--------

```js
stream = new TextDecoderStream();
console.log(stream.fatal); // returns false
```

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

| Specification |
| --- |
| Encoding Standard # dom-textdecoder-fatal |

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

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


TextDecoderStream: encoding property - Web APIs

TextDecoderStream: encoding property
====================================

The **`encoding`** read-only property of the `TextDecoderStream` interface returns a string containing the name of the encoding algorithm used by the specific decoder.

The encoding is set by the constructor `label` parameter, and defaults to `utf-8`.

Value
-----

A string containing the lower-cased ASCII name of the encoding format.

The allowed values are the same as those listed in `TextDecoder.encoding` (the labels in Encoding API Encodings).

Examples
--------

Returning the value of `encoding` from a `TextDecoderStream`.

```js
stream = new TextDecoderStream();
console.log(stream.encoding); // returns the default "utf-8"
```

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

| Specification |
| --- |
| Encoding Standard # dom-textdecoder-encoding |

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

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


TextDecoderStream: writable property - Web APIs

TextDecoderStream: writable property
====================================

The **`writable`** read-only property of the `TextDecoderStream` interface returns a `WritableStream`.

Value
-----

A `WritableStream`.

Examples
--------

Returning a `WritableStream` from a `TextDecoderStream`.

```js
stream = new TextDecoderStream();
console.log(stream.writable); // A WritableStream
```

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

| Specification |
| --- |
| Streams Standard # dom-generictransformstream-writable |

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

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


TextDecoderStream: ignoreBOM property - Web APIs

TextDecoderStream: ignoreBOM property
=====================================

The **`ignoreBOM`** read-only property of the `TextDecoderStream` interface is a `Boolean` indicating whether the byte order mark will be included in the output or skipped over.

Value
-----

`true` if the byte order mark will be included in the decoded text; `false` if it will be skipped over when decoding and omitted.

Examples
--------

```js
stream = new TextDecoderStream();
console.log(stream.ignoreBOM); // returns false
```

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

| Specification |
| --- |
| Encoding Standard # textdecoder-ignore-bom-flag |

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

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


Using VR controllers with WebVR - Web APIs

Using VR controllers with WebVR
===============================

**Deprecated:** This feature is no longer recommended. Though some browsers might still support it, it may have already been removed from the relevant web standards, may be in the process of being dropped, or may only be kept for compatibility purposes. Avoid using it, and update existing code if possible; see the compatibility table at the bottom of this page to guide your decision. Be aware that this feature may cease to work at any time.

Many WebVR hardware setups feature controllers that go along with the headset. These can be used in WebVR apps via the Gamepad API, and specifically the Gamepad Extensions API that adds API features for accessing controller pose, haptic actuators, and more. This article explains the basics.

**Note:** WebVR API is replaced by WebXR API. WebVR was never ratified as a standard, was implemented and enabled by default in very few browsers and supported a small number of devices.

The WebVR API
-------------

The WebVR API is a nascent, but very interesting new feature of the web platform that allows developers to create web-based virtual reality experiences. It does this by providing access to VR headsets connected to your computer as `VRDisplay` objects, which can be manipulated to start and stop presentation to the display, queried for movement data (e.g. orientation and position) that can be used to update the display on each frame of the animation loop, and more.

Before you read this article, you should really be familiar with the basics of the WebVR API already — go and read Using the WebVR API first, if you haven't already done so, which also details browser support and required hardware setup.

The Gamepad API
---------------

The Gamepad API is a fairly well-supported API that allows developers to access gamepads/controllers connected to your computer and use them to control web apps. The basic Gamepad API provides access to connected controllers as `Gamepad` objects, which can then be queried to find out what buttons are being pressed and thumbsticks (axes) are being moved at any point, etc.

You can find more about basic Gamepad API usage in Using the Gamepad API, and Implementing controls using the Gamepad API.

However, in this article we will mainly be concentrating on some of the new features provided by the Gamepad Extensions API, which allows access to advanced controller information such as position and orientation data, control over haptic actuators (e.g. vibration hardware), and more. This API is very new, and currently is only supported and enabled by default in Firefox 55+ Beta/Nightly channels.

Types of controller
-------------------

There are two types of controller you'll encounter with VR hardware:

* 6DoF (six-degrees-of-freedom) controllers provide access to both positional and orientation data — they can manipulate a VR scene and the objects it contains with movement but also rotation. A good example is the HTC VIVE controllers.
* 3DoF (three-degrees-of-freedom) controllers provide orientation but not positional data. A good example is the Google Daydream controller, which can be rotated to point to different things in 3D space like a laser pointer, but can't be moved inside a 3D scene.
Basic controller access
-----------------------

Now onto some code. Let's look first at the basics of how we get access to VR controllers with the Gamepad API. There are a few strange nuances to bear in mind here, so it is worth taking a look.

We've written up a simple example to demonstrate — see our vr-controller-basic-info source code (see it running live here also). This demo outputs information on the VR displays and gamepads connected to your computer.

### Getting the display information

The first notable code is as follows:

```js
let initialRun = true;

if (navigator.getVRDisplays && navigator.getGamepads) {
  info.textContent = "WebVR API and Gamepad API supported.";
  reportDisplays();
} else {
  info.textContent =
    "WebVR API and/or Gamepad API not supported by this browser.";
}
```

Here we first use a tracking variable, `initialRun`, to note that this is the first time we have loaded the page. You'll find out more about this later on. Next, we detect to see if the WebVR and Gamepad APIs are supported by checking for the existence of the `Navigator.getVRDisplays()` and `Navigator.getGamepads()` methods. If so, we run our `reportDisplays()` custom function to start the process off. This function looks like so:

```js
function reportDisplays() {
  navigator.getVRDisplays().then((displays) => {
    console.log(`${displays.length} displays`);
    displays.forEach((display, i) => {
      const cap = display.capabilities;
      // cap is a VRDisplayCapabilities object
      const listItem = document.createElement("li");
      listItem.innerHTML =
        `<strong>Display ${i + 1}</strong><br>` +
        `VR Display ID: ${display.displayId}<br>` +
        `VR Display Name: ${display.displayName}<br>` +
        `Display can present content: ${cap.canPresent}<br>` +
        `Display is separate from the computer's main display: ${cap.hasExternalDisplay}<br>` +
        `Display can return position info: ${cap.hasPosition}<br>` +
        `Display can return orientation info: ${cap.hasOrientation}<br>` +
        `Display max layers: ${cap.maxLayers}`;
      list.appendChild(listItem);
    });

    setTimeout(reportGamepads, 1000);
    // For VR, controllers will only be active after their corresponding headset is active
  });
}
```

This function first uses the promise-based `Navigator.getVRDisplays()` method, which resolves with an array containing `VRDisplay` objects representing the connected displays. Next, it prints out each display's `VRDisplay.displayId` and `VRDisplay.displayName` values, and a number of useful values contained in the display's associated `VRCapabilities` object. The most useful of these are `hasOrientation` and `hasPosition`, which allow you to detect whether the device can return orientation and position data and set up your app accordingly.

The last line contained in this function is a `setTimeout()` call, which runs the `reportGamepads()` function after a 1 second delay. Why do we need to do this? First of all, VR controllers will only be ready after their associated VR headset is active, so we need to invoke this after `getVRDisplays()` has been called and returned the display information. Second, the Gamepad API is much older than the WebVR API, and not promise-based. As you'll see later, the `getGamepads()` method is synchronous, and just returns the `Gamepad` objects immediately — it doesn't wait for the controller to be ready to report information. Unless you wait for a little while, returned information may not be accurate (at least, this is what we found in our tests).

### Getting the Gamepad information

The `reportGamepads()` function looks like this:

```js
function reportGamepads() {
  const gamepads = navigator.getGamepads();
  console.log(`${gamepads.length} controllers`);
  for (const gp of gamepads) {
    const listItem = document.createElement("li");
    listItem.classList = "gamepad";
    listItem.innerHTML =
      `<strong>Gamepad ${gp.index}</strong> (${gp.id})<br>` +
      `Associated with VR Display ID: ${gp.displayId}<br>` +
      `Gamepad associated with which hand: ${gp.hand}<br>` +
      `Available haptic actuators: ${gp.hapticActuators.length}<br>` +
      `Gamepad can return position info: ${gp.pose.hasPosition}<br>` +
      `Gamepad can return orientation info: ${gp.pose.hasOrientation}`;
    list.appendChild(listItem);
  }
  initialRun = false;
}
```

This works in a similar manner to `reportDisplays()` — we get an array of `Gamepad` objects using the non-promise-based `getGamepads()` method, then cycle through each one and print out information on each:

* The `Gamepad.displayId` property is the same as the `displayId` of the headset the controller is associated with, and therefore useful for tying controller and headset information together.
* The `Gamepad.index` property is unique numerical index that identifies each connected controller.
* `Gamepad.hand` returns which hand the controller is expected to be held in.
* `Gamepad.hapticActuators` returns an array of the haptic actuators available in the controller. Here we are returning its length so we can see how many each has available.
* Finally, we return `GamepadPose.hasPosition` and `GamepadPose.hasOrientation` to show whether the controller can return position and orientation data. This works just the same as for the displays, except that in the case of gamepads these values are available on the pose object, not the capabilities object.

Note that we also gave each list item containing controller information a class name of `gamepad`. We'll explain what this is for later.

The last thing to do here is set the `initialRun` variable to `false`, as the initial run is now over.

### Gamepad events

To finish off this section, we'll look at the gamepad-associated events. There are two we need concern ourselves with — `gamepadconnected` and `gamepaddisconnected` — and it is fairly obvious what they do.

At the end of our example we first include the `removeGamepads()` function:

```js
function removeGamepads() {
  const gpLi = document.querySelectorAll(".gamepad");
  for (let i = 0; i < gpLi.length; i++) {
    list.removeChild(gpLi[i]);
  }
  reportGamepads();
}
```

This function grabs references to all list items with a class name of `gamepad`, and removes them from the DOM. Then it re-runs `reportGamepads()` to populate the list with the updated list of connected controllers.

`removeGamepads()` will be run each time a gamepad is connected or disconnected, via the following event handlers:

```js
window.addEventListener("gamepadconnected", (e) => {
  info.textContent = `Gamepad ${e.gamepad.index} connected.`;
  if (!initialRun) {
    setTimeout(removeGamepads, 1000);
  }
});

window.addEventListener("gamepaddisconnected", (e) => {
  info.textContent = `Gamepad ${e.gamepad.index} disconnected.`;
  setTimeout(removeGamepads, 1000);
});
```

We have `setTimeout()` calls in place here — like we did with the initialization code at the top of the script — to make sure that the gamepads are ready to report their information when `reportGamepads()` is called in each case.

But there's one more thing to note — you'll see that inside the `gamepadconnected` handler, the timeout is only run if `initialRun` is `false`. This is because if your gamepads are connected when the document first loads, `gamepadconnected` is fired once for each gamepad, therefore `removeGamepads()`/`reportGamepads()` will be run several times. This could lead to inaccurate results, therefore we only want to run `removeGamepads()` inside the `gamepadconnected` handler after the initial run, not during it. This is what `initialRun` is for.

Introducing a real demo
-----------------------

Now let's look at the Gamepad API being used inside a real WebVR demo. You can find this demo at raw-webgl-controller-example (see it live here also).

In exactly the same way as our raw-webgl-example (see Using the WebVR API for details), this renders a spinning 3D cube, which you can choose to present in a VR display. The only difference is that, while in VR presenting mode, this demo allows you to move the cube by moving a VR controller (the original demo moves the cube as you move your VR headset).

We'll explore the code differences in this version below — see webgl-demo.js.

### Accessing the gamepad data

Inside the `drawVRScene()` function, you'll find this bit of code:

```js
const gamepads = navigator.getGamepads();
const gp = gamepads[0];

if (gp) {
  const gpPose = gp.pose;
  const curPos = gpPose.position;
  const curOrient = gpPose.orientation;
  if (poseStatsDisplayed) {
    displayPoseStats(gpPose);
  }
}
```

Here we get the connected gamepads with `Navigator.getGamepads`, then store the first gamepad detected in the `gp` variable. As we only need one gamepad for this demo, we'll just ignore the others.

The next thing we do is to get the `GamepadPose` object for the controller stored in gpPose (by querying `Gamepad.pose`), and also store the current gamepad position and orientation for this frame in variables so they are easy to access later. We also display the post stats for this frame in the DOM using the `displayPoseStats()` function. All of this is only done if `gp` actually has a value (if a gamepad is connected), which stops the demo erroring if we don't have our gamepad connected.

Slightly later in the code, you can find this block:

```js
if (gp && gpPose.hasPosition) {
  mvTranslate([
    0.0 + curPos[0] \* 15 - curOrient[1] \* 15,
    0.0 + curPos[1] \* 15 + curOrient[0] \* 15,
    -15.0 + curPos[2] \* 25,
  ]);
} else if (gp) {
  mvTranslate([0.0 + curOrient[1] \* 15, 0.0 + curOrient[0] \* 15, -15.0]);
} else {
  mvTranslate([0.0, 0.0, -15.0]);
}
```

Here we alter the position of the cube on the screen according to the `position` and `orientation` data received from the connected controller. These values (stored in `curPos` and `curOrient`) are `Float32Array`s containing the X, Y, and Z values (here we are just using [0] which is X, and [1] which is Y).

If the `gp` variable has a `Gamepad` object inside it and it can return position values (`gpPose.hasPosition`), indicating a 6DoF controller, we modify the cube position using position and orientation values. If only the former is true, indicating a 3DoF controller, we modify the cube position using the orientation values only. If there is no gamepad connected, we don't modify the cube position at all.

### Displaying the gamepad pose data

In the `displayPoseStats()` function, we grab all of the data we want to display out of the `GamepadPose` object passed into it, then print them into the UI panel that exists in the demo for displaying such data:

```js
function displayPoseStats(pose) {
  const pos = pose.position;

  const formatCoords = ([x, y, z]) =>
    `x ${x.toFixed(3)}, y ${y.toFixed(3)}, z ${z.toFixed(3)}`;

  posStats.textContent = pose.hasPosition
    ? `Position: ${formatCoords(pose.position)}`
    : "Position not reported";

  orientStats.textContent = pose.hasOrientation
    ? `Orientation: ${formatCoords(pose.orientation)}`
    : "Orientation not reported";

  linVelStats.textContent = `Linear velocity: ${formatCoords(
 pose.linearVelocity,
 )}`;
  angVelStats.textContent = `Angular velocity: ${formatCoords(
 pose.angularVelocity,
 )}`;

  linAccStats.textContent = pose.linearAcceleration
    ? `Linear acceleration: ${formatCoords(pose.linearAcceleration)}`
    : "Linear acceleration not reported";

  angAccStats.textContent = pose.angularAcceleration
    ? `Angular acceleration: ${formatCoords(pose.angularAcceleration)}`
    : "Angular acceleration not reported";
}
```

Summary
-------

This article has given you a very basic idea of how to use the Gamepad Extensions to use VR controllers inside WebVR apps. In a real app you'd probably have a much more complex control system in effect, with controls assigned to the buttons on the VR controllers, and the display being affected by both the display pose and the controller poses simultaneously. Here however, we just wanted to isolate the pure Gamepad Extensions parts of that.

See also
--------

* WebVR API
* Gamepad API
* Using the WebVR API
* Implementing controls using the Gamepad API


Using the WebVR API - Web APIs

Using the WebVR API
===================

**Deprecated:** This feature is no longer recommended. Though some browsers might still support it, it may have already been removed from the relevant web standards, may be in the process of being dropped, or may only be kept for compatibility purposes. Avoid using it, and update existing code if possible; see the compatibility table at the bottom of this page to guide your decision. Be aware that this feature may cease to work at any time.

**Note:** WebVR API is replaced by WebXR API. WebVR was never ratified as a standard, was implemented and enabled by default in very few browsers and supported a small number of devices.

The WebVR API is a fantastic addition to the web developer's toolkit, allowing WebGL scenes to be presented in virtual reality displays such as the Oculus Rift and HTC Vive. But how do you get started with developing VR apps for the Web? This article will guide you through the basics.

Getting started
---------------

To get started, you need:

* Supporting VR hardware.
    + The cheapest option is to use a mobile device, supporting browser, and device mount (e.g. Google Cardboard). This won't be quite as good an experience as dedicated hardware, but you won't need to purchase a powerful computer or dedicated VR display.
    + Dedicated hardware can be costly, but it does provide a better experience. The most WebVR-compatible hardware at the moment is the HTC VIVE, and The Oculus Rift. The front page of webvr.info has some further useful information about available hardware, and what browser support them.
* A computer powerful enough to handle rendering/displaying of VR scenes using your dedicated VR Hardware, if required. To give you an idea of what you need, look at the relevant guide for the VR you are purchasing (e.g. VIVE READY Computers).
* A supporting browser installed — the latest Firefox Nightly or Chrome are your best options right now, on desktop or mobile.

Once you have everything assembled, you can test to see whether your setup works with WebVR by going to our simple A-Frame demo, and seeing whether the scene renders and whether you can enter VR display mode by pressing the button at the bottom right.

A-Frame is by far the best option if you want to create a WebVR-compatible 3D scene quickly, without needing to understand a bunch of new JavaScript code. It doesn't however teach you how the raw WebVR API works, and this is what we'll get on to next.

Introducing our demo
--------------------

To illustrate how the WebVR API works, we'll study our raw-webgl-example, which looks a bit like this:

![A gray rotating 3D cube](/en-US/docs/Web/API/WebVR_API/Using_the_WebVR_API/capture1.png)

**Note:** You can find the source code of our demo on GitHub, and view it live also.

**Note:** If WebVR isn't working in your browser, you might need to make sure it is running through your graphics card. For example for NVIDIA cards, if you've got the NVIDIA control panel set up successfully, there will be a context menu option available — right click on Firefox, then choose *Run with graphics processor > High-performance NVIDIA processor*.

Our demo features the holy grail of WebGL demos — a rotating 3D cube. We've implemented this using raw WebGL API code. We won't be teaching any basic JavaScript or WebGL, just the WebVR parts.

Our demo also features:

* A button to start (and stop) our scene from being presented in the VR display.
* A button to show (and hide) VR pose data, i.e. the position and orientation of the headset, updated in real time.

When you look through the source code of our demo's main JavaScript file, you can easily find the WebVR-specific parts by searching for the string "WebVR" in preceding comments.

**Note:** To find out more about basic JavaScript and WebGL, consult our JavaScript learning material, and our WebGL Tutorial.

How does it work?
-----------------

At this point, let's look at how the WebVR parts of the code work.

A typical (simple) WebVR app works like this:

1. `Navigator.getVRDisplays()` is used to get a reference to your VR display.
2. `VRDisplay.requestPresent()` is used to start presenting to the VR display.
3. WebVR's dedicated `VRDisplay.requestAnimationFrame()` method is used to run the app's rendering loop at the correct refresh rate for the display.
4. Inside the rendering loop, you grab the data required to display the current frame (`VRDisplay.getFrameData()`), draw the displayed scene twice — once for the view in each eye — then submit the rendered view to the display to show to the user via (`VRDisplay.submitFrame()`).

In the below sections we'll look at our raw-webgl-demo in detail, and see where exactly the above features are used.

### Starting with some variables

The first WebVR-related code you'll meet is this following block:

```js
// WebVR variables

const frameData = new VRFrameData();
let vrDisplay;
const btn = document.querySelector(".stop-start");
let normalSceneFrame;
let vrSceneFrame;

const poseStatsBtn = document.querySelector(".pose-stats");
const poseStatsSection = document.querySelector("section");
poseStatsSection.style.visibility = "hidden"; // hide it initially

const posStats = document.querySelector(".pos");
const orientStats = document.querySelector(".orient");
const linVelStats = document.querySelector(".lin-vel");
const linAccStats = document.querySelector(".lin-acc");
const angVelStats = document.querySelector(".ang-vel");
const angAccStats = document.querySelector(".ang-acc");
let poseStatsDisplayed = false;
```

Let's briefly explain these:

* `frameData` contains a `VRFrameData` object, created using the `VRFrameData()` constructor. This is initially empty, but will later contain the data required to render each frame to show in the VR display, updated constantly as the rendering loop runs.
* `vrDisplay` starts uninitialized, but will later on hold a reference to our VR headset (`VRDisplay` — the central control object of the API).
* `btn` and `poseStatsBtn` hold references to the two buttons we are using to control our app.
* `normalSceneFrame` and `vrSceneFrame` start uninitialized, but later on will hold references to `Window.requestAnimationFrame()` and `VRDisplay.requestAnimationFrame()` calls — these will initiate the running of a normal rendering loop, and a special WebVR rendering loop; we'll explain the difference between these two later on.
* The other variables store references to different parts of the VR pose data display box, which you can see in the bottom right-hand corner of the UI.
### Getting a reference to our VR display

One of the major functions inside our code is `start()` — we run this function when the body has finished loading:

```js
// start
//
// Called when the body has loaded is created to get the ball rolling.

document.body.onload = start;
```

To begin with, `start()` retrieves a WebGL context to use to render 3D graphics into the `<canvas>` element in our HTML. We then check whether the `gl` context is available — if so, we run a number of functions to set up the scene for display.

```js
function start() {
  canvas = document.getElementById("glcanvas");

  initWebGL(canvas);      // Initialize the GL context

  // WebGL setup code here
```

Next, we start the process of actually rendering the scene onto the canvas, by setting the canvas to fill the entire browser viewport, and running the rendering loop (`drawScene()`) for the first time. This is the non-WebVR — normal — rendering loop.

```js
// draw the scene normally, without WebVR - for those who don't have it and want to see the scene in their browser

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
drawScene();
```

Now onto our first WebVR-specific code. First of all, we check to see if `Navigator.getVRDisplays` exists — this is the entry point into the API, and therefore good basic feature detection for WebVR. You'll see at the end of the block (inside the `else` clause) that if this doesn't exist, we log a message to indicate that WebVR 1.1 isn't supported by the browser.

```js
  // WebVR: Check to see if WebVR is supported
  if (navigator.getVRDisplays) {
    console.log('WebVR 1.1 supported');
```

Inside our `if () { }` block, we run the `Navigator.getVRDisplays()` function. This returns a promise, which is fulfilled with an array containing all the VR display devices connected to the computer. If none are connected, the array will be empty.

```js
    // Then get the displays attached to the computer
    navigator.getVRDisplays().then((displays) => {
```

Inside the promise `then()` block, we check whether the array length is more than 0; if so, we set the value of our `vrDisplay` variable to the 0 index item inside the array. `vrDisplay` now contains a `VRDisplay` object representing our connected display!

```js
      // If a display is available, use it to present the scene
      if (displays.length > 0) {
        vrDisplay = displays[0];
        console.log('Display found');
```

**Note:** It is unlikely that you'll have multiple VR displays connected to your computer, and this is just a simple demo, so this will do for now.

### Starting and stopping the VR presentation

Now we have a `VRDisplay` object, we can use it do a number of things. The next thing we want to do is wire up functionality to start and stop presentation of the WebGL content to the display.

Continuing on with the previous code block, we now add an event listener to our start/stop button (`btn`) — when this button is clicked we want to check whether we are presenting to the display already (we do this in a fairly dumb fashion, by checking what the button `textContent` contains).

If the display is not already presenting, we use the `VRDisplay.requestPresent()` method to request that the browser start presenting content to the display. This takes as a parameter an array of the `VRLayerInit` objects representing the layers you want to present in the display.

Since the maximum number of layers you can display is currently 1, and the only required object member is the `VRLayerInit.source` property (which is a reference to the `<canvas>` you want to present in that layer; the other parameters are given sensible defaults — see `leftBounds` and `rightBounds`)), the parameter is [{ source: canvas }].

`requestPresent()` returns a promise that is fulfilled when the presentation begins successfully.

```js
        // Starting the presentation when the button is clicked: It can only be called in response to a user gesture
        btn.addEventListener('click', () => {
          if (btn.textContent === 'Start VR display') {
            vrDisplay.requestPresent([{ source: canvas }]).then(() => {
              console.log('Presenting to WebVR display');
```

With our presentation request successful, we now want to start setting up to render content to present to the VRDisplay. First of all we set the canvas to the same size as the VR display area. We do this by getting the `VREyeParameters` for both eyes using `VRDisplay.getEyeParameters()`.

We then do some simple math to calculate the total width of the VRDisplay rendering area based on the eye `VREyeParameters.renderWidth` and `VREyeParameters.renderHeight`.

```js
// Set the canvas size to the size of the vrDisplay viewport

const leftEye = vrDisplay.getEyeParameters("left");
const rightEye = vrDisplay.getEyeParameters("right");

canvas.width = Math.max(leftEye.renderWidth, rightEye.renderWidth) \* 2;
canvas.height = Math.max(leftEye.renderHeight, rightEye.renderHeight);
```

Next, we `cancel the animation loop` previously set in motion by the `Window.requestAnimationFrame()` call inside the `drawScene()` function, and instead invoke `drawVRScene()`. This function renders the same scene as before, but with some special WebVR magic going on. The loop inside here is maintained by WebVR's special `VRDisplay.requestAnimationFrame` method.

```js
// stop the normal presentation, and start the vr presentation
window.cancelAnimationFrame(normalSceneFrame);
drawVRScene();
```

Finally, we update the button text so that the next time it is pressed, it will stop presentation to the VR display.

```js
              btn.textContent = 'Exit VR display';
            });
```

To stop the VR presentation when the button is subsequently pressed, we call `VRDisplay.exitPresent()`. We also reverse the button's text content, and swap over the `requestAnimationFrame` calls. You can see here that we are using `VRDisplay.cancelAnimationFrame` to stop the VR rendering loop, and starting the normal rendering loop off again by calling `drawScene()`.

```js
          } else {
            vrDisplay.exitPresent();
            console.log('Stopped presenting to WebVR display');

            btn.textContent = 'Start VR display';

            // Stop the VR presentation, and start the normal presentation
            vrDisplay.cancelAnimationFrame(vrSceneFrame);
            drawScene();
          }
        });
      }
    });
  } else {
    console.log('WebVR API not supported by this browser.');
  }
}
```

Once the presentation starts, you'll be able to see the stereoscopic view displayed in the browser:

![Stereoscopic view of 3D cube](/en-US/docs/Web/API/WebVR_API/Using_the_WebVR_API/capture2.png)

You'll learn below how the stereoscopic view is actually produced.

### Why does WebVR have its own requestAnimationFrame()?

This is a good question. The reason is that for smooth rendering inside the VR display, you need to render the content at the display's native refresh rate, not that of the computer. VR display refresh rates are greater than PC refresh rates, typically up to 90fps. The rate will be differ from the computer's core refresh rate.

Note that when the VR display is not presenting, `VRDisplay.requestAnimationFrame` runs identically to `Window.requestAnimationFrame`, so if you wanted, you could just use a single rendering loop, rather than the two we are using in our app. We have used two because we wanted to do slightly different things depending on whether the VR display is presenting or not, and keep things separated for ease of comprehension.

### Rendering and display

At this point, we've seen all the code required to access the VR hardware, request that we present our scene to the hardware, and start running the rending loop. Let's now look at the code for the rendering loop, and explain how the WebVR-specific parts of it work.

First of all, we begin the definition of our rendering loop function — `drawVRScene()`. The first thing we do inside here is make a call to `VRDisplay.requestAnimationFrame()` to keep the loop running after it has been called once (this occurred earlier on in our code when we started presenting to the VR display). This call is set as the value of the global `vrSceneFrame` variable, so we can cancel the loop with a call to `VRDisplay.cancelAnimationFrame()` once we exit VR presenting.

```js
function drawVRScene() {
  // WebVR: Request the next frame of the animation
  vrSceneFrame = vrDisplay.requestAnimationFrame(drawVRScene);
```

Next, we call `VRDisplay.getFrameData()`, passing it the name of the variable that we want to use to contain the frame data. We initialized this earlier on — `frameData`. After the call completes, this variable will contain the data need to render the next frame to the VR device, packaged up as a `VRFrameData` object. This contains things like projection and view matrices for rendering the scene correctly for the left and right eye view, and the current `VRPose` object, which contains data on the VR display such as orientation, position, etc.

This has to be called on every frame so the rendered view is always up-to-date.

```js
// Populate frameData with the data of the next frame to display
vrDisplay.getFrameData(frameData);
```

Now we retrieve the current `VRPose` from the `VRFrameData.pose` property, store the position and orientation for use later on, and send the current pose to the pose stats box for display, if the `poseStatsDisplayed` variable is set to true.

```js
// You can get the position, orientation, etc. of the display from the current frame's pose

const curFramePose = frameData.pose;
const curPos = curFramePose.position;
const curOrient = curFramePose.orientation;
if (poseStatsDisplayed) {
  displayPoseStats(curFramePose);
}
```

We now clear the canvas before we start drawing on it, so that the next frame is clearly seen, and we don't also see previous rendered frames:

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

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

We now render the view for both the left and right eyes. First of all we need to create projection and view locations for use in the rendering. These are `WebGLUniformLocation` objects, created using the `WebGLRenderingContext.getUniformLocation()` method, passing it the shader program's identifier and an identifying name as parameters.

```js
// WebVR: Create the required projection and view matrix locations needed
// for passing into the uniformMatrix4fv methods below

const projectionMatrixLocation = gl.getUniformLocation(
  shaderProgram,
  "projMatrix",
);
const viewMatrixLocation = gl.getUniformLocation(shaderProgram, "viewMatrix");
```

The next rendering step involves:

* Specifying the viewport size for the left eye, using `WebGLRenderingContext.viewport` — this is logically the first half of the canvas width, and the full canvas height.
* Specifying the view and projection matrix values to use to render the left eye — this is done using the `WebGLRenderingContext.uniformMatrix4fv` method, which is passed the location values we retrieved above, and the left matrices obtained from the `VRFrameData` object.
* Running the `drawGeometry()` function, which renders the actual scene — because of what we specified in the previous two steps, we will render it for the left eye only.

```js
// WebVR: Render the left eye's view to the left half of the canvas
gl.viewport(0, 0, canvas.width \* 0.5, canvas.height);
gl.uniformMatrix4fv(
  projectionMatrixLocation,
  false,
  frameData.leftProjectionMatrix,
);
gl.uniformMatrix4fv(viewMatrixLocation, false, frameData.leftViewMatrix);
drawGeometry();
```

We now do exactly the same thing, but for the right eye:

```js
// WebVR: Render the right eye's view to the right half of the canvas
gl.viewport(canvas.width \* 0.5, 0, canvas.width \* 0.5, canvas.height);
gl.uniformMatrix4fv(
  projectionMatrixLocation,
  false,
  frameData.rightProjectionMatrix,
);
gl.uniformMatrix4fv(viewMatrixLocation, false, frameData.rightViewMatrix);
drawGeometry();
```

Next we define our `drawGeometry()` function. Most of this is just general WebGL code required to draw our 3D cube. You'll see some WebVR-specific parts in the `mvTranslate()` and `mvRotate()` function calls — these pass matrices into the WebGL program that define the translation and rotation of the cube for the current frame

You'll see that we are modifying these values by the position (`curPos`) and orientation (`curOrient`) of the VR display we got from the `VRPose` object. The result is that, for example, as you move or rotate your head left, the x position value (`curPos[0]`) and y rotation value (`[curOrient[1]`) are added to the x translation value, meaning that the cube will move to the right, as you'd expect when you are looking at something and then move/turn your head left.

This is a quick and dirty way to use VR pose data, but it illustrates the basic principle.

```js
function drawGeometry() {
  // Establish the perspective with which we want to view the
  // scene. Our field of view is 45 degrees, with a width/height
  // ratio of 640:480, and we only want to see objects between 0.1 units
  // and 100 units away from the camera.
  perspectiveMatrix = makePerspective(45, 640.0 / 480.0, 0.1, 100.0);

  // Set the drawing position to the "identity" point, which is
  // the center of the scene.
  loadIdentity();

  // Now move the drawing position a bit to where we want to start
  // drawing the cube.
  mvTranslate([
    0.0 - curPos[0] \* 25 + curOrient[1] \* 25,
    5.0 - curPos[1] \* 25 - curOrient[0] \* 25,
    -15.0 - curPos[2] \* 25,
  ]);

  // Save the current matrix, then rotate before we draw.
  mvPushMatrix();
  mvRotate(cubeRotation, [0.25, 0, 0.25 - curOrient[2] \* 0.5]);

  // Draw the cube by binding the array buffer to the cube's vertices
  // array, setting attributes, and pushing it to GL.
  gl.bindBuffer(gl.ARRAY\_BUFFER, cubeVerticesBuffer);
  gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);

  // Set the texture coordinates attribute for the vertices.
  gl.bindBuffer(gl.ARRAY\_BUFFER, cubeVerticesTextureCoordBuffer);
  gl.vertexAttribPointer(textureCoordAttribute, 2, gl.FLOAT, false, 0, 0);

  // Specify the texture to map onto the faces.
  gl.activeTexture(gl.TEXTURE0);
  gl.bindTexture(gl.TEXTURE\_2D, cubeTexture);
  gl.uniform1i(gl.getUniformLocation(shaderProgram, "uSampler"), 0);

  // Draw the cube.
  gl.bindBuffer(gl.ELEMENT\_ARRAY\_BUFFER, cubeVerticesIndexBuffer);
  setMatrixUniforms();
  gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED\_SHORT, 0);

  // Restore the original matrix
  mvPopMatrix();
}
```

The next bit of the code has nothing to do with WebVR — it just updates the rotation of the cube on each frame:

```js
// Update the rotation for the next draw, if it's time to do so.
let currentTime = new Date().getTime();
if (lastCubeUpdateTime) {
  const delta = currentTime - lastCubeUpdateTime;

  cubeRotation += (30 \* delta) / 1000.0;
}
lastCubeUpdateTime = currentTime;
```

The last part of the rendering loop involves us calling `VRDisplay.submitFrame()` — now all the work has been done and we've rendered the display on the `<canvas>`, this method then submits the frame to the VR display so it is displayed on there as well.

```js
  // WebVR: Indicate that we are ready to present the rendered frame to the VR display
  vrDisplay.submitFrame();
}
```

### Displaying the pose (position, orientation, etc.) data

In this section we'll discuss the `displayPoseStats()` function, which displays our updated pose data on each frame. The function is fairly simple.

First of all, we store the six different property values obtainable from the `VRPose` object in their own variables — each one is a `Float32Array`.

```js
function displayPoseStats(pose) {
  const pos = pose.position;
  const orient = pose.orientation;
  const linVel = pose.linearVelocity;
  const linAcc = pose.linearAcceleration;
  const angVel = pose.angularVelocity;
  const angAcc = pose.angularAcceleration;
```

We then write out the data into the information box, updating it on every frame. We've clamped each value to three decimal places using `toFixed()`, as the values are hard to read otherwise.

You should note that we've used a conditional expression to detect whether the linear acceleration and angular acceleration arrays are successfully returned before we display the data. These values are not reported by most VR hardware as yet, so the code would throw an error if we did not do this (the arrays return `null` if they are not successfully reported).

```js
  posStats.textContent = `Position: ` +
    `x ${pos[0].toFixed(3)}, ` +
    `y ${pos[1].toFixed(3)}, ` +
    `z ${pos[2].toFixed(3)}`;
  orientStats.textContent = `Orientation: ` +
    `x ${orient[0].toFixed(3)}, ` +
    `y ${orient[1].toFixed(3)}, ` +
    `z ${orient[2].toFixed(3)}`;
  linVelStats.textContent = `Linear velocity: ` +
    `x ${linVel[0].toFixed(3)}, ` +
    `y ${linVel[1].toFixed(3)}, ` +
    `z ${linVel[2].toFixed(3)}`;
  angVelStats.textContent = `Angular velocity: ` +
    `x ${angVel[0].toFixed(3)}, ` +
    `y ${angVel[1].toFixed(3)}, ` +
    `z ${angVel[2].toFixed(3)}`;

  if (linAcc) {
    linAccStats.textContent = `Linear acceleration: ` +
      `x ${linAcc[0].toFixed(3)}, ` +
      `y ${linAcc[1].toFixed(3)}, ` +
      `z ${linAcc[2].toFixed(3)}`;
  } else {
    linAccStats.textContent = 'Linear acceleration not reported';
  }

  if (angAcc) {
    angAccStats.textContent = `Angular acceleration: ` +
    `x ${angAcc[0].toFixed(3)}, ` +
    `y ${angAcc[1].toFixed(3)}, ` +
    `z ${angAcc[2].toFixed(3)}`;
  } else {
    angAccStats.textContent = 'Angular acceleration not reported';
  }
}
```

WebVR events
------------

The WebVR spec features a number of events that are fired, allowing our app code to react to changes in the state of the VR display (see Window events). For example:

* `vrdisplaypresentchange` — Fires when the presenting state of a VR display changes — i.e. goes from presenting to not presenting, or vice versa.
* `vrdisplayconnect` — Fires when a compatible VR display has been connected to the computer.
* `vrdisplaydisconnect` — Fires when a compatible VR display has been disconnected from the computer.

To demonstrate how they work, our simple demo includes the following example:

```js
window.addEventListener("vrdisplaypresentchange", (e) => {
  console.log(
    `Display ${e.display.displayId} presentation has changed. Reason given: ${e.reason}.`,
  );
});
```

As you can see, the `event object` provides two useful properties — `VRDisplayEvent.display`, which contains a reference to the `VRDisplay` the event was fired in response to, and `VRDisplayEvent.reason`, which contains a human-readable reason why the event was fired.

This is a very useful event; you could use it to handle cases where the display gets disconnected unexpectedly, stopping errors from being thrown and making sure the user is aware of the situation. In Google's Webvr.info presentation demo, the event is used to run an `onVRPresentChange()` function, which updates the UI controls as appropriate and resizes the canvas.

Summary
-------

This article has given you the very basics of how to create a simple WebVR 1.1 app, to help you get started.


WebVR concepts - Web APIs

WebVR concepts
==============

**Deprecated:** This feature is no longer recommended. Though some browsers might still support it, it may have already been removed from the relevant web standards, may be in the process of being dropped, or may only be kept for compatibility purposes. Avoid using it, and update existing code if possible; see the compatibility table at the bottom of this page to guide your decision. Be aware that this feature may cease to work at any time.

**Note:** WebVR API is replaced by WebXR API. WebVR was never ratified as a standard, was implemented and enabled by default in very few browsers and supported a small number of devices.

This article discusses some of the concepts and theory behind virtual reality (VR). If you are a newcomer to the area, it is worthwhile getting an understanding of these topics before you start diving into code.

The history of VR
-----------------

Virtual reality is nothing new — the concept goes way further back than the Oculus Rift Kickstarter campaign of 2012. People have been experimenting with it for decades.

In 1939 the View-Master device was created, allowing people to see 3D pictures. The device displayed images stored on cardboard disks containing stereoscopic 3D pairs of small color photographs. After years of development the military got interested in using such technology, and Project Headsight was born in 1961 — this involved a helmet incorporating a video screen with a head-tracking system.

There were various experiments conducted over the next few decades, but it wasn't restricted to science labs and battlefields anymore. Eventually pop culture took over with movie directors showing their visions of virtual reality. Movies like Tron (1982) and The Matrix (1999) were created, where people could transfer themselves into a whole new cyber world or were trapped in one without even knowing, accepting it as the real world.

The first VR gaming attempts were big and expensive — in 1991 Virtuality Group created a VR-ready arcade machine with goggles and ported popular titles like Pac-Man to virtual reality. Sega introduced their VR glasses at the Consumer Electronics Show in 1993. Companies were experimenting, but the market and consumers weren't convinced — we had to wait until 2012 to see a real example of a successful VR project.

### VR in recent times

So what's new? Virtual Reality hardware needs to deliver high-precision, low-latency data to deliver an acceptable user experience; computers running VR applications need to be powerful enough to handle all this information. It has not been until recently that such accuracy and power has been available at an affordable cost, if at all. Early VR prototypes cost tens of thousands of dollars, whereas more recent headsets such as the HTC VIVE and Oculus Rift are available for hundreds of dollars, and cheaper solutions are available — mobile device-based solutions like Gear VR and Google Cardboard.

On the software side, Valve has created SteamVR software, which is compatible with the VIVE and other solutions, and serves to provide access to software, such as a usable VR UI.

The technology itself is here, and the more expensive headsets will only get cheaper over time so more people can experience virtual reality on their own in the future.

### Input devices

Handling input for virtual reality applications is an interesting topic — it's a totally new experience for which dedicated user interfaces have to be designed. There are various approaches right now from classic keyboard and mouse, to new ones like Leap Motion and the VIVE controllers. It's a matter of trial and error to see what works in given situations and what inputs fit best for your type of game.

VR Hardware setup
-----------------

There are two main types of setup, mobile or computer-connected. Their minimum hardware set ups are as follows:

* Mobile: A Head-mounted display (HMD) is created using a smartphone — which acts as the VR display — mounted in a VR mount such as Google Cardboard, which contains the required lenses to provide stereoscopic vision of what is projected on the mobile screen.
 ![Mobile based VR setup](/en-US/docs/Web/API/WebVR_API/Concepts/mobilebasedvrsetup.png)
* Computer-connected: A VR setup is connected to your computer — this consists of a Head Mounted Display (HMD) containing a high resolution landscape-oriented screen onto which the visuals for both the left and right eye are displayed, which also includes a lens for each eye to promote separation of the left and right eye scene (stereoscopic vision.) The setup also includes a separate position sensor that works out the position/orientation/velocity/acceleration of your head and constantly passes that information the computer.
 ![Computer based VR Setup](/en-US/docs/Web/API/WebVR_API/Concepts/computerbasedvrsetup.png)

**Note:** Computer-connected systems sometimes don't include a position sensor, but they usually do.

Other hardware that complements the VR experience includes:

* **A hand recognition sensor**: A sensor that tracks the position and movement of your hand, allowing it to become an interesting controller, and an object in VR gameworlds. The most advanced to date is the Leap Motion, which works with the computer (connected to the Oculus Rift) and can also work connected to a mobile device (the latter is in an experimental phase.)
* **A gamepad**: We can configure an XBox controller or similar to work as a keyboard in the browser — this offers further possibilities of interaction with a VR webpage. There are some gamepads that work with a mobile setup but these are connected via Bluetooth so don't work with WebVR.
* **An eye tracking sensor (experimental)**: The FOVE project is the first headset that reads subtle eye movements.
* **A facial expression tracker (experimental)**: Researchers at the University of Southern California and Facebook's Oculus division have been testing new ways of tracking facial expressions and transferring them to a virtual character.
* **A more complex positional sensor system**: As an example, the HTC VIVE features two position sensors that sit in opposite corners of a space, mapping it all out and allowing VR experiences to be enjoyed in spaces of up to 5m x 5m.
Position and orientation, velocity and acceleration
---------------------------------------------------

As mentioned above, the position sensor detects information concerning the HMD and constantly outputs it, allowing you to continually update a scene according to head movement, rotation, etc. But what exactly is the information?

![Position and Orientation VR setup](/en-US/docs/Web/API/WebVR_API/Concepts/positionorientationvr.png)

The output information falls into four categories:

1. Position — The position of the HMD along three axes in a 3D coordinate space. x is to the left and right, y is up and down, and z is towards and away from the position sensor. In WebVR, the x, y, and z coordinates are represented by the array contained in `VRPose.position`.
2. Orientation — The rotation of the HMD around three axes in a 3D coordinate space. Pitch is rotation around the x axis, yaw is rotation around the y axis, and roll is rotation around the z axis. In WebVR, the pitch, yaw, and roll are represented by the first three elements of the array contained in `VRPose.orientation`.
3. Velocity — There are two types of velocity to consider in VR:
    * Linear — The speed along any one of the axes that the HMD is traveling. This information can be accessed using `VRPose.linearVelocity`.
    * Angular — The speed at which the HMD is rotating around any one of the axes. This information can be accessed using `VRPose.angularVelocity`.
4. Acceleration — There are two types of acceleration to consider in VR:
    * Linear — The acceleration of travel along any one of the axes that the HMD is traveling. This information can be accessed using `VRPose.linearAcceleration`.
    * Angular — The acceleration of rotation of the HMD around any one of the axes. This information can be accessed using `VRPose.angularAcceleration`.
Field of view
-------------

The field of view (FOV) is the area that each of the user's eyes can reasonably be expected to see. It roughly takes the form of a pyramid shape, laid down on one side, with the apex inside the user's head, and the rest of the pyramid emanating from the user's eye. Each eye has its own FOV, one slightly overlapping the other.

![FOV related properties](/en-US/docs/Web/API/WebVR_API/Concepts/fovrelatedproperties.png)

The FOV is defined by the following values:

* `VRFieldOfView.upDegrees`: The number of degrees upwards that the field of view extends in.
* `VRFieldOfView.rightDegrees`: The number of degrees to the right that the field of view extends in.
* `VRFieldOfView.downDegrees`: The number of degrees downwards that the field of view extends in.
* `VRFieldOfView.leftDegrees`: The number of degrees to the left that the field of view extends in.
* zNear, defined by `VRDisplay.depthNear`: The distance from the middle of the user's head to the start of the visible FOV.
* zFar, defined by `VRDisplay.depthFar`: The distance from the middle of the user's head to the end of the visible FOV.

The default values for these properties will differ slightly by VR hardware, although they tend to be around 53° up and down, and 47° left and right, with zNear and zFar coming in at around 0.1m and 10000m respectively.

**Note:** The user can potentially see all the way around them, which is a brand new concept for apps and games. Try to give people a reason to look around and see what's behind them — make them reach out and find things that are not visible at the very beginning. Describe what's behind their backs.

Concepts for VR apps
--------------------

This section discusses concepts to be aware of when developing VR apps that you've probably not had to consider before when developing regular apps for mobile or desktop.

### Stereoscopic vision

Stereoscopic vision is the normal vision humans and (most) animals have — the perception of two slightly differing images (one from each eye) as a single image. This results in depth perception, helping us to see the world in glorious 3D. To recreate this in VR apps, you need to render two very slightly different views side by side, which will be taken in by the left and right eyes when the user is using the HMD.

![How to create stereoscopic 3D images](/en-US/docs/Web/API/WebVR_API/Concepts/createstereoscopicimages.png)

### Head tracking

 The primary technology used to make you feel present in a 360º scene, thanks to the gyroscope, accelerometer, and magnetometer (compass) included in the HMD.
 It has primary relevance because it makes our eyes believe we are in front of a spherical screen, giving users realistic immersion inside the app canvas.

### Eye strain

A term commonly used in VR because it is a major handicap of using an HMD — we are constantly fooling the eye with what we are showing in the app canvas, and this leads to the eyes doing a lot more work than they normally would, so using VR apps for any extended period of time can lead to eye strain.

To minimize this unwanted effect, we need to:

* Avoid focusing on different depths (e.g. avoid using a lot of particles with different depths.)
* Avoid eye convergence (e.g. if you have an object that comes towards the camera your eyes will follow and converge on it.)
* Use darker backgrounds with more subdued colors where possible; a bright screen will make the eyes more tired.
* Avoid rapid brightness changes.
* Avoid presenting the user with large amounts of text to read. You should also be careful with the distance between the eyes/camera and the text to read. 0.5m is uncomfortable, whereas at more than 2m the stereo effect starts to break down, so somewhere in between is advisable.
* Be careful with the distance between objects and the camera in general. Oculus recommends 0.75m as a minimum distance of focus.
* Use a pointer if the user needs to interact with an object in the scene — this will help them point to it correctly with less effort.

In general, the path of least visual effort will give the user a less tiring experience.

### Motion sickness

If developers do not take utmost care, VR apps can actually cause their users to feel sick. This effect is produced when the stimuli their eyes are receiving is not what the body expects to receive.

To avoid bringing on motion sickness in our users (or at least minimize the effects), we need to:

* Always maintain head tracking (this is the most important of all, especially if it occurs in middle of the experience.)
* Use constant velocity; avoid acceleration or deceleration camera movements (use linear acceleration, and avoid easing if you can.)
* Keep the frame rate up (less than 30fps is uncomfortable.)
* Avoid sharp and/or unexpected camera rotations.
* Add fixed points of reference for fixed objects (otherwise the user will believe they are on the move.)
* Do not use Depth of Field or Motion Blur post processing because you do not know where the eyes will focus.
* Avoid brightness changes (use low frequency textures or fog effects to create smooth lighting transitions).

Overall your eyes should not send signals to the brain that cause reflex actions in other parts of the body.

### Latency

Latency is the time between the physical head movement and the visual display reaching the user's eyes from the screen of an HMD being updated. This is one of the most critical factors in providing a realistic experience. Humans can detect very small delays — we need to keep the latency below 20 milliseconds if they are to be imperceptible (for example a 60Hz monitor has a 16 ms response.)

The Oculus Rift headset has a latency of 20 ms or less, but with mobile device-based setups it will depend heavily on the smartphone CPU power and other capabilities.

### Frame rate (Frames per second / FPS)

Based on the Wikipedia definition, frame rate is the frequency at which an imaging device produces unique consecutive images, called frames. A rate of 60fps is an acceptable rate for a smooth user experience, but depending on the performance of the machine the app is running on, or the complexity of the content you want to show, it can drastically lower. Less than 30fps is generally considered jittery, and annoying to the user.

One of the most difficult tasks is to maintain a constant and high frame rate value, so we must optimize our code to make it as efficient as possible. It is preferable to have a decent frame rate that doesn't constantly or suddenly change; for this you need to as few necessary objects moving into the scene as possible and (in the case of WebGL) try to reduce draw calls.

### Interpupillary distance (IPD)

Based on the Wikipedia definition, IPD is the distance between the centers of the pupils of the two eyes. IPD is critical for the design of binocular viewing systems, where both eye pupils need to be positioned within the exit pupils of the viewing system.

Interpupillary distance (IPD) can be calculated using `VREyeParameters.offset` in WebVR, which is equal to half the IPD.

This value is returned by the HMD and its value may be around 60 to 70 mm; in the case of some HMDs like Oculus Rift's, you can set your own IPD. Normally we don't change this value but you can play with it to change the scale of the entire scene. For example, if your IPD is set to 6000 mm, the user would view the scene like a giant looking at a Lilliputian world.

### Degrees of Freedom (DoF)

DoF refers to the movement of a rigid body inside space. There is no uniformity in creating acronyms for this term — we can find references to 3DoF in the context of sensors that detect only rotational head tracking, and 6DoF when an input allows us to control position and orientation simultaneously. We even sometimes find 9DoF references when the hardware contains three sensors like gyroscope, accelerometer and magnetometer, but the results of the 3 x 3DoF values will actually return a 6 degrees of freedom tracking.

DoF is directly related to the tracking of the user's head movement.

### Cone of focus

Although our field of view is much larger (approximately 180º), we need to be aware that only in a small portion of that field can you perceive symbols (the center 60º) or read text (the center 10º). If you do not have an eye tracking sensor we assume that the center of the screen is where the user is focusing their eyes.

This limitation is important to consider when deciding where to place visuals on the app canvas — too far toward the edge of the cone of focus can lead to eye strain much more quickly. Read very interesting post Quick VR Mockups with Illustrator.

### 3D Positional Audio

3D positional audio refers to a group of effects that manipulate audio to simulate how it would sound in a three dimensional space.

This directly related to the Web Audio API, which allows us to place sounds on objects we have in the canvas or launch audio depending on the part of the scene the user is traveling towards or looking at.


Traversing an HTML table with JavaScript and DOM Interfaces - Web APIs

Traversing an HTML table with JavaScript and DOM Interfaces
===========================================================

This article is an overview of some powerful, fundamental DOM level 1 methods and how to use them from JavaScript. You will learn how to create, access and control, and remove HTML elements dynamically. The DOM methods presented here are not specific to HTML; they also apply to XML. The demonstrations provided here will work fine in any modern browser.

**Note:** The DOM methods presented here are part of the Document Object Model (Core) level 1 specification. DOM level 1 includes both methods for generic document access and manipulation (DOM 1 Core) as well as methods specific to HTML documents (DOM 1 HTML).

Creating an HTML table dynamically
----------------------------------

### Example

In this example we add a new table to the page when a button is clicked.

#### HTML

```html
<input type="button" value="Generate a table" onclick="generateTable()" />
```

#### JavaScript

```js
function generateTable() {
  // creates a <table> element and a <tbody> element
  const tbl = document.createElement("table");
  const tblBody = document.createElement("tbody");

  // creating all cells
  for (let i = 0; i < 2; i++) {
    // creates a table row
    const row = document.createElement("tr");

    for (let j = 0; j < 2; j++) {
      // Create a <td> element and a text node, make the text
      // node the contents of the <td>, and put the <td> at
      // the end of the table row
      const cell = document.createElement("td");
      const cellText = document.createTextNode(`cell in row ${i}, column ${j}`);
      cell.appendChild(cellText);
      row.appendChild(cell);
    }

    // add the row to the end of the table body
    tblBody.appendChild(row);
  }

  // put the <tbody> in the <table>
  tbl.appendChild(tblBody);
  // appends <table> into <body>
  document.body.appendChild(tbl);
  // sets the border attribute of tbl to '2'
  tbl.setAttribute("border", "2");
}
```

```
table {
  margin: 1rem auto;
}

td {
  padding: 0.5rem;
}
```

#### Result

### Explanation

Note the order in which we created the elements and the text node:

1. First we created the `<table>` element.
2. Next, we created the `<tbody>` element, which is a child of the `<table>` element.
3. Next, we used a loop to create the `<tr>` elements, which are children of the `<tbody>` element.
4. For each `<tr>` element, we used a loop to create the `<td>` elements, which are children of `<tr>` elements.
5. For each `<td>` element, we then created the text node with the table cell's text.

Once we have created the `<table>`, `<tbody>`, `<tr>`, and `<td>` elements, and then the text node, we then append each object to its parent in the opposite order:

1. First, we attach each text node to its parent `<td>` element using

```js
cell.appendChild(cellText);
```

2. Next, we attach each `<td>` element to its parent `<tr>` element using

```js
row.appendChild(cell);
```

3. Next, we attach each `<tr>` element to the parent `<tbody>` element using

```js
tblBody.appendChild(row);
```

4. Next, we attach the `<tbody>` element to its parent `<table>` element using

```js
tbl.appendChild(tblBody);
```

5. Next, we attach the `<table>` element to its parent `<body>` element using

```js
document.body.appendChild(tbl);
```

Remember this technique. You will use it frequently in programming for the W3C DOM. First, you create elements from the top down; then you attach the children to the parents from the bottom up.

Here's the HTML markup generated by the JavaScript code:

```html
<table border="2">
  <tbody>
    <tr>
      <td>cell is row 0 column 0</td>
      <td>cell is row 0 column 1</td>
    </tr>
    <tr>
      <td>cell is row 1 column 0</td>
      <td>cell is row 1 column 1</td>
    </tr>
  </tbody>
</table>
```

Here's the DOM object tree generated by the code for the `<table>` element and its child elements:

![How a DOM object tree is generated from the main element and its children](/en-US/docs/Web/API/Document_Object_Model/Traversing_an_HTML_table_with_JavaScript_and_DOM_Interfaces/sample1-tabledom.jpg)

You can build this table and its internal child elements by using just a few DOM methods. Remember to keep in mind the tree model for the structures you are planning to create; this will make it easier to write the necessary code. In the `<table>` tree of Figure 1 the element `<table>` has one child: the element `<tbody>`. `<tbody>` has two children. Each `<tbody>`'s child (`<tr>`) has two children (`<td>`). Finally, each `<td>` has one child: a text node.

Setting the background color of a paragraph
-------------------------------------------

### Example

In this example we change the background color of a paragraph when a button is clicked.

#### HTML

```html
<body>
  <input
 type="button"
 value="Set paragraph background color"
 onclick="setBackground()" />
  <p>hi</p>
  <p>hello</p>
</body>
```

#### JavaScript

```js
function setBackground() {
  // now, get all the p elements in the document
  const paragraphs = document.getElementsByTagName("p");

  // get the second paragraph from the list
  const secondParagraph = paragraphs[1];

  // set the inline style
  secondParagraph.style.background = "red";
}
```

#### Result

### Explanation

`getElementsByTagName(tagNameValue)` is a method available in any DOM `Element` or the root `Document` element. When called, it returns an array with all of the element's descendants matching the tag name. The first element of the list is located at position `[0]` in the array.

We've performed following steps:

1. First, we get all the `p` elements in the document:

```js
const paragraphs = document.getElementsByTagName("p");
```

2. Then we get the second paragraph element from the list of `p` elements:

```js
const secondParagraph = paragraphs[1];
```

![A paragraph element is added as a new sibling to an existing paragraph in a DOM tree](/en-US/docs/Web/API/Document_Object_Model/Traversing_an_HTML_table_with_JavaScript_and_DOM_Interfaces/sample2a2.jpg)
3. Finally, we set background color to red using the `style` property of the `paragraph` object:

```js
secondParagraph.style.background = "red";
```

### Creating TextNodes with document.createTextNode("..")

Use the document object to invoke the `createTextNode` method and create your text node. You just need to pass the text content. The return value is an object that represents the text node.

```js
myTextNode = document.createTextNode("world");
```

This means that you have created a node of the type `TEXT_NODE` (a piece of text) whose text data is `"world"`, and `myTextNode` is your reference to this node object. To insert this text into your HTML page, you need to make this text node a child of some other node element.

### Inserting Elements with appendChild(..)

So, by calling `secondParagraph.appendChild(node_element)`, you are making the element a new child of the second `<p>` element.

```js
secondParagraph.appendChild(myTextNode);
```

After testing this sample, note that the words hello and world are together: helloworld. So visually, when you see the HTML page it seems like the two text nodes hello and world are a single node, but remember that in the document model, there are two nodes. The second node is a new node of type `TEXT_NODE`, and it is the second child of the second `<p>` tag. The following figure shows the recently created Text Node object inside the document tree.

![Text nodes in a paragraph element as individual siblings in the DOM tree.](/en-US/docs/Web/API/Document_Object_Model/Traversing_an_HTML_table_with_JavaScript_and_DOM_Interfaces/sample2b2.jpg)

**Note:** `createTextNode()` and `appendChild()` is a simple way to include white space between the words *hello* and *world*. Another important note is that the `appendChild` method will append the child after the last child, just like the word *world* has been added after the word *hello*. So if you want to append a text node between *hello* and *world*, you will need to use `insertBefore` instead of `appendChild`.

### Creating New Elements with the document object and the createElement(..) method

You can create new HTML elements or any other element you want with `createElement`. For example, if you want to create a new `<p>` element as a child of the `<body>` element, you can use the `myBody` in the previous example and append a new element node. To create a node call `document.createElement("tagname")`. For example:

```js
myNewPTagNode = document.createElement("p");
myBody.appendChild(myNewPTagNode);
```

![How a new node element is appended to the text node object inside the document tree](/en-US/docs/Web/API/Document_Object_Model/Traversing_an_HTML_table_with_JavaScript_and_DOM_Interfaces/sample2c.jpg)

### Removing nodes with the removeChild(..) method

Nodes can be removed. The following code removes text node `myTextNode` (containing the word "world") from the second `<p>` element, `secondParagraph`.

```js
secondParagraph.removeChild(myTextNode);
```

Text node `myTextNode` (containing the word "world") still exists. The following code attaches `myTextNode` to the recently created `<p>` element, `myNewPTagNode`.

```js
myNewPTagNode.appendChild(myTextNode);
```

The final state for the modified object tree looks like this:

![Creating and appending a new node element to the object tree text structure](/en-US/docs/Web/API/Document_Object_Model/Traversing_an_HTML_table_with_JavaScript_and_DOM_Interfaces/sample2d.jpg)

Creating a table dynamically (back to Sample1.html)
---------------------------------------------------

For the rest of this article we will continue working with sample1.html. The following figure shows the table object tree structure for the table created in the sample.

### Reviewing the HTML Table structure

![The HTML table object tree structure after adding new node elements](/en-US/docs/Web/API/Document_Object_Model/Traversing_an_HTML_table_with_JavaScript_and_DOM_Interfaces/sample1-tabledom.jpg)

### Creating element nodes and inserting them into the document tree

The basic steps to create the table in sample1.html are:

* Get the body object (first item of the document object).
* Create all the elements.
* Finally, append each child according to the table structure (as in the above figure). The following source code is a commented version for the sample1.html.

**Note:** At the end of the `start` function, there is a new line of code. The table's `border` property was set using another DOM method, `setAttribute()`. `setAttribute()` has two arguments: the attribute name and the attribute value. You can set any attribute of any element using the `setAttribute` method.

```html
<html lang="en">
  <head>
    <title>
      Sample code - Traversing an HTML Table with JavaScript and DOM Interfaces
    </title>
    <script>
 function start() {
 // get the reference for the body
 const myBody = document.getElementsByTagName("body")[0];

 // creates <table> and <tbody> elements
 const myTable = document.createElement("table");
 const myTableBody = document.createElement("tbody");

 // creating all cells
 for (let j = 0; j < 3; j++) {
 // creates a <tr> element
 const myCurrentRow = document.createElement("tr");

 for (let i = 0; i < 4; i++) {
 // creates a <td> element
 const myCurrentCell = document.createElement("td");
 // creates a Text Node
 const currentText = document.createTextNode(
 `cell is row ${j}, column ${i}`,
 );
 // appends the Text Node we created into the cell <td>
 myCurrentCell.appendChild(currentText);
 // appends the cell <td> into the row <tr>
 myCurrentRow.appendChild(myCurrentCell);
 }
 // appends the row <tr> into <tbody>
 myTableBody.appendChild(myCurrentRow);
 }

 // appends <tbody> into <table>
 myTable.appendChild(myTableBody);
 // appends <table> into <body>
 myBody.appendChild(myTable);
 // sets the border attribute of myTable to 2;
 myTable.setAttribute("border", "2");
 }
 </script>
  </head>
  <body onload="start()"></body>
</html>
```

Manipulating the table with DOM and CSS
---------------------------------------

### Getting a text node from the table

This example introduces two new DOM attributes. First it uses the `childNodes` attribute to get the list of child nodes of myCell. The `childNodes` list includes all child nodes, regardless of what their name or type is. Like `getElementsByTagName()`, it returns a list of nodes.

The differences are that (a) `getElementsByTagName()` only returns elements of the specified tag name; and (b) `childNodes` includes all descendants at any level, not just immediate children.

Once you have the returned list, use `[x]` method to retrieve the desired child item. This example stores in `myCellText` the text node of the second cell in the second row of the table.

Then, to display the results in this example, it creates a new text node whose content is the data of `myCellText`, and appends it as a child of the `<body>` element.

**Note:** If your object is a text node, you can use the data attribute and retrieve the text content of the node.

```js
myBody = document.getElementsByTagName("body")[0];
myTable = myBody.getElementsByTagName("table")[0];
myTableBody = myTable.getElementsByTagName("tbody")[0];
myRow = myTableBody.getElementsByTagName("tr")[1];
myCell = myRow.getElementsByTagName("td")[1];

// first item element of the childNodes list of myCell
myCellText = myCell.childNodes[0];

// content of currentText is the data content of myCellText
currentText = document.createTextNode(myCellText.data);
myBody.appendChild(currentText);
```

### Getting an attribute value

At the end of sample1 there is a call to `setAttribute` on the `myTable` object. This call was used to set the border property of the table. To retrieve the value of the attribute, use the `getAttribute` method:

```js
myTable.getAttribute("border");
```

### Hiding a column by changing style properties

Once you have the object in your JavaScript variable, you can set `style` properties directly. The following code is a modified version of sample1.html in which each cell of the second column is hidden and each cell of the first column is changed to have a red background. Note that the `style` property was set directly.

```html
<html lang="en">
  <body onload="start()"></body>
  <script>
 function start() {
 const myBody = document.getElementsByTagName("body")[0];
 const myTable = document.createElement("table");
 const myTableBody = document.createElement("tbody");

 for (let row = 0; row < 2; row++) {
 const myCurrentRow = document.createElement("tr");
 for (let col = 0; col < 2; col++) {
 const myCurrentCell = document.createElement("td");
 const currentText = document.createTextNode(`cell is: ${row}${col}`);
 myCurrentCell.appendChild(currentText);
 myCurrentRow.appendChild(myCurrentCell);
 // set the cell background color
 // if the column is 0. If the column is 1 hide the cell
 if (col === 0) {
 myCurrentCell.style.background = "rgb(255 0 0)";
 } else {
 myCurrentCell.style.display = "none";
 }
 }
 myTableBody.appendChild(myCurrentRow);
 }
 myTable.appendChild(myTableBody);
 myBody.appendChild(myTable);
 }
 </script>
</html>
```