Taking still photos with getUserMedia() - Web APIs

Taking still photos with getUserMedia()
=======================================

This article shows how to use `navigator.mediaDevices.getUserMedia()` to access the camera on a computer or mobile phone with `getUserMedia()` support and take a photo with it.

![getUserMedia-based image capture app — on the left we have a video stream taken from a webcam and a take photo button, on the right we have the still image output from taking the photo](/en-US/docs/Web/API/Media_Capture_and_Streams_API/Taking_still_photos/web-rtc-demo.png)

You can also jump straight to the Demo if you like.

The HTML markup
---------------

Our HTML interface has two main operational sections: the stream and capture panel and the presentation panel. Each of these is presented side-by-side in its own `<div>` to facilitate styling and control.

The first panel on the left contains two components: a `<video>` element, which will receive the stream from `navigator.mediaDevices.getUserMedia()`, and a `<button>` the user clicks to capture a video frame.

```html
<div class="camera">
  <video id="video">Video stream not available.</video>
  <button id="startbutton">Take photo</button>
</div>
```

This is straightforward, and we'll see how it ties together when we get into the JavaScript code.

Next, we have a `<canvas>` element into which the captured frames are stored, potentially manipulated in some way, and then converted into an output image file. This canvas is kept hidden by styling the canvas with `display``:none`, to avoid cluttering up the screen — the user does not need to see this intermediate stage.

We also have an `<img>` element into which we will draw the image — this is the final display shown to the user.

```html
<canvas id="canvas"> </canvas>
<div class="output">
  <img id="photo" alt="The screen capture will appear in this box." />
</div>
```

That's all of the relevant HTML. The rest is just some page layout fluff and a bit of text offering a link back to this page.

The JavaScript code
-------------------

Now let's take a look at the JavaScript code. We'll break it up into a few bite-sized pieces to make it easier to explain.

### Initialization

We start by wrapping the whole script in an anonymous function to avoid global variables, then setting up various variables we'll be using.

```js
(() => {
  const width = 320;    // We will scale the photo width to this
  const height = 0;     // This will be computed based on the input stream

  const streaming = false;

  let video = null;
  let canvas = null;
  let photo = null;
  let startbutton = null;
```

Those variables are:

`width`

Whatever size the incoming video is, we're going to scale the resulting image to be 320 pixels wide.

`height`

The output height of the image will be computed given the `width` and the aspect ratio of the stream.

`streaming`

Indicates whether or not there is currently an active stream of video running.

`video`

This will be a reference to the `<video>` element after the page is done loading.

`canvas`

This will be a reference to the `<canvas>` element after the page is done loading.

`photo`

This will be a reference to the `<img>` element after the page is done loading.

`startbutton`

This will be a reference to the `<button>` element that's used to trigger capture. We'll get that after the page is done loading.

### The startup() function

The `startup()` function is run when the page has finished loading, courtesy of `EventTarget.addEventListener`. This function's job is to request access to the user's webcam, initialize the output `<img>` to a default state, and to establish the event listeners needed to receive each frame of video from the camera and react when the button is clicked to capture an image.

#### Getting element references

First, we grab references to the major elements we need to be able to access.

```js
  function startup() {
    video = document.getElementById('video');
    canvas = document.getElementById('canvas');
    photo = document.getElementById('photo');
    startbutton = document.getElementById('startbutton');
```

#### Get the media stream

The next task is to get the media stream:

```js
navigator.mediaDevices
  .getUserMedia({ video: true, audio: false })
  .then((stream) => {
    video.srcObject = stream;
    video.play();
  })
  .catch((err) => {
    console.error(`An error occurred: ${err}`);
  });
```

Here, we're calling `MediaDevices.getUserMedia()` and requesting a video stream (without audio). It returns a promise which we attach success and failure callbacks to.

The success callback receives a `stream` object as input. It is the `<video>` element's source to our new stream.

Once the stream is linked to the `<video>` element, we start it playing by calling `HTMLMediaElement.play()`.

The error callback is called if opening the stream doesn't work. This will happen for example if there's no compatible camera connected, or the user denied access.

#### Listen for the video to start playing

After calling `HTMLMediaElement.play()` on the `<video>`, there's a (hopefully brief) period of time that elapses before the stream of video begins to flow. To avoid blocking until that happens, we add an event listener to `video` for the `canplay` event, which is delivered when the video playback actually begins. At that point, all the properties in the `video` object have been configured based on the stream's format.

```js
video.addEventListener(
  "canplay",
  (ev) => {
    if (!streaming) {
      height = (video.videoHeight / video.videoWidth) \* width;

      video.setAttribute("width", width);
      video.setAttribute("height", height);
      canvas.setAttribute("width", width);
      canvas.setAttribute("height", height);
      streaming = true;
    }
  },
  false,
);
```

This callback does nothing unless it's the first time it's been called; this is tested by looking at the value of our `streaming` variable, which is `false` the first time this method is run.

If this is indeed the first run, we set the video's height based on the size difference between the video's actual size, `video.videoWidth`, and the width at which we're going to render it, `width`.

Finally, the `width` and `height` of both the video and the canvas are set to match each other by calling `Element.setAttribute()` on each of the two properties on each element, and setting widths and heights as appropriate. Finally, we set the `streaming` variable to `true` to prevent us from inadvertently running this setup code again.

#### Handle clicks on the button

To capture a still photo each time the user clicks the `startbutton`, we need to add an event listener to the button, to be called when the `click` event is issued:

```js
startbutton.addEventListener(
  "click",
  (ev) => {
    takepicture();
    ev.preventDefault();
  },
  false,
);
```

This method is simple enough: it just calls our `takepicture()` function, defined below in the section Capturing a frame from the stream, then calls `Event.preventDefault()` on the received event to prevent the click from being handled more than once.

#### Wrapping up the startup() method

There are only two more lines of code in the `startup()` method:

```js
    clearphoto();
  }
```

This is where we call the `clearphoto()` method we'll describe below in the section Clearing the photo box.

### Clearing the photo box

Clearing the photo box involves creating an image, then converting it into a format usable by the `<img>` element that displays the most recently captured frame. That code looks like this:

```js
function clearphoto() {
  const context = canvas.getContext("2d");
  context.fillStyle = "#AAA";
  context.fillRect(0, 0, canvas.width, canvas.height);

  const data = canvas.toDataURL("image/png");
  photo.setAttribute("src", data);
}
```

We start by getting a reference to the hidden `<canvas>` element that we use for offscreen rendering. Next we set the `fillStyle` to `#AAA` (a fairly light grey), and fill the entire canvas with that color by calling `fillRect()`.

Last in this function, we convert the canvas into a PNG image and call `photo.setAttribute()` to make our captured still box display the image.

### Capturing a frame from the stream

There's one last function to define, and it's the point to the entire exercise: the `takepicture()` function, whose job it is to capture the currently displayed video frame, convert it into a PNG file, and display it in the captured frame box. The code looks like this:

```js
function takepicture() {
  const context = canvas.getContext("2d");
  if (width && height) {
    canvas.width = width;
    canvas.height = height;
    context.drawImage(video, 0, 0, width, height);

    const data = canvas.toDataURL("image/png");
    photo.setAttribute("src", data);
  } else {
    clearphoto();
  }
}
```

As is the case any time we need to work with the contents of a canvas, we start by getting the `2D drawing context` for the hidden canvas.

Then, if the width and height are both non-zero (meaning that there's at least potentially valid image data), we set the width and height of the canvas to match that of the captured frame, then call `drawImage()` to draw the current frame of the video into the context, filling the entire canvas with the frame image.

**Note:** This takes advantage of the fact that the `HTMLVideoElement` interface looks like an `HTMLImageElement` to any API that accepts an `HTMLImageElement` as a parameter, with the video's current frame presented as the image's contents.

Once the canvas contains the captured image, we convert it to PNG format by calling `HTMLCanvasElement.toDataURL()` on it; finally, we call `photo.setAttribute()` to make our captured still box display the image.

If there isn't a valid image available (that is, the `width` and `height` are both 0), we clear the contents of the captured frame box by calling `clearphoto()`.

Demo
----

### HTML

```html
<div class="contentarea">
  <h1>MDN - navigator.mediaDevices.getUserMedia(): Still photo capture demo</h1>
  <p>
    This example demonstrates how to set up a media stream using your built-in
    webcam, fetch an image from that stream, and create a PNG using that image.
  </p>
  <div class="camera">
    <video id="video">Video stream not available.</video>
    <button id="startbutton">Take photo</button>
  </div>
  <canvas id="canvas"> </canvas>
  <div class="output">
    <img id="photo" alt="The screen capture will appear in this box." />
  </div>
  <p>
    Visit our article
    <a
 href="https://developer.mozilla.org/en-US/docs/Web/API/Media\_Capture\_and\_Streams\_API/Taking\_still\_photos">
      Taking still photos with WebRTC</a
 >
    to learn more about the technologies used here.
  </p>
</div>
```

### CSS

```css
#video {
  border: 1px solid black;
  box-shadow: 2px 2px 3px black;
  width: 320px;
  height: 240px;
}

#photo {
  border: 1px solid black;
  box-shadow: 2px 2px 3px black;
  width: 320px;
  height: 240px;
}

#canvas {
  display: none;
}

.camera {
  width: 340px;
  display: inline-block;
}

.output {
  width: 340px;
  display: inline-block;
  vertical-align: top;
}

#startbutton {
  display: block;
  position: relative;
  margin-left: auto;
  margin-right: auto;
  bottom: 32px;
  background-color: rgb(0 150 0 / 50%);
  border: 1px solid rgb(255 255 255 / 70%);
  box-shadow: 0px 0px 1px 2px rgb(0 0 0 / 20%);
  font-size: 14px;
  font-family: "Lucida Grande", "Arial", sans-serif;
  color: rgb(255 255 255 / 100%);
}

.contentarea {
  font-size: 16px;
  font-family: "Lucida Grande", "Arial", sans-serif;
  width: 760px;
}
```

### JavaScript

```js
(() => {
  // The width and height of the captured photo. We will set the
  // width to the value defined here, but the height will be
  // calculated based on the aspect ratio of the input stream.

  const width = 320; // We will scale the photo width to this
  let height = 0; // This will be computed based on the input stream

  // |streaming| indicates whether or not we're currently streaming
  // video from the camera. Obviously, we start at false.

  let streaming = false;

  // The various HTML elements we need to configure or control. These
  // will be set by the startup() function.

  let video = null;
  let canvas = null;
  let photo = null;
  let startbutton = null;

  function showViewLiveResultButton() {
    if (window.self !== window.top) {
      // Ensure that if our document is in a frame, we get the user
      // to first open it in its own tab or window. Otherwise, it
      // won't be able to request permission for camera access.
      document.querySelector(".contentarea").remove();
      const button = document.createElement("button");
      button.textContent = "View live result of the example code above";
      document.body.append(button);
      button.addEventListener("click", () => window.open(location.href));
      return true;
    }
    return false;
  }

  function startup() {
    if (showViewLiveResultButton()) {
      return;
    }
    video = document.getElementById("video");
    canvas = document.getElementById("canvas");
    photo = document.getElementById("photo");
    startbutton = document.getElementById("startbutton");

    navigator.mediaDevices
      .getUserMedia({ video: true, audio: false })
      .then((stream) => {
        video.srcObject = stream;
        video.play();
      })
      .catch((err) => {
        console.error(`An error occurred: ${err}`);
      });

    video.addEventListener(
      "canplay",
      (ev) => {
        if (!streaming) {
          height = video.videoHeight / (video.videoWidth / width);

          // Firefox currently has a bug where the height can't be read from
          // the video, so we will make assumptions if this happens.

          if (isNaN(height)) {
            height = width / (4 / 3);
          }

          video.setAttribute("width", width);
          video.setAttribute("height", height);
          canvas.setAttribute("width", width);
          canvas.setAttribute("height", height);
          streaming = true;
        }
      },
      false,
    );

    startbutton.addEventListener(
      "click",
      (ev) => {
        takepicture();
        ev.preventDefault();
      },
      false,
    );

    clearphoto();
  }

  // Fill the photo with an indication that none has been
  // captured.

  function clearphoto() {
    const context = canvas.getContext("2d");
    context.fillStyle = "#AAA";
    context.fillRect(0, 0, canvas.width, canvas.height);

    const data = canvas.toDataURL("image/png");
    photo.setAttribute("src", data);
  }

  // Capture a photo by fetching the current contents of the video
  // and drawing it into a canvas, then converting that to a PNG
  // format data URL. By drawing it on an offscreen canvas and then
  // drawing that to the screen, we can change its size and/or apply
  // other changes before drawing it.

  function takepicture() {
    const context = canvas.getContext("2d");
    if (width && height) {
      canvas.width = width;
      canvas.height = height;
      context.drawImage(video, 0, 0, width, height);

      const data = canvas.toDataURL("image/png");
      photo.setAttribute("src", data);
    } else {
      clearphoto();
    }
  }

  // Set up our event listener to run the startup process
  // once loading is complete.
  window.addEventListener("load", startup, false);
})();
```

### Result

Fun with filters
----------------

Since we're capturing images from the user's webcam by grabbing frames from a `<video>` element, we can very easily apply filters and fun effects to the video. As it turns out, any CSS filters you apply to the element using the `filter` property affect the captured photo. These filters can range from the simple (making the image black and white) to the extreme (gaussian blurs and hue rotation).

You can play with this effect using, for example, the Firefox developer tools' style editor; see Edit CSS filters for details on how to do so.

Using specific devices
----------------------

You can, if needed, restrict the set of permitted video sources to a specific device or set of devices. To do so, call `MediaDevices.enumerateDevices`. When the promise is fulfilled with an array of `MediaDeviceInfo` objects describing the available devices, find the ones that you want to allow and specify the corresponding `deviceId` or `deviceId`s in the `MediaTrackConstraints` object passed into `getUserMedia()`.

See also
--------

* Sample code on GitHub
* `MediaDevices.getUserMedia`
* Using frames from a video in the Canvas tutorial
* `CanvasRenderingContext2D.drawImage()`


Capabilities, constraints, and settings - Web APIs

Capabilities, constraints, and settings
=======================================

This article discusses the twin concepts of **constraints** and **capabilities**, as well as media settings, and includes an example we call the Constraint Exerciser. The Constraint Exerciser lets you experiment with the results of different constraint sets being applied to the audio and video tracks coming from the computer's A/V input devices (such as its webcam and microphone).

Historically, writing scripts for the Web that work intimately with Web APIs has had a well-known challenge: often, your code needs to know whether or not an API exists and if so, what its limitations are on the user agent it's running on. Figuring this out has often been difficult, and has usually involved looking at some combination of which user agent (or browser) you're running on, which version it is, looking to see if certain objects exist, trying to see whether various things work or not and determining what errors occur, and so forth. The result has been a lot of very fragile code, or a reliance on libraries which figure this stuff out for you, then implement polyfills to patch the holes in the implementation on your behalf.

Capabilities and constraints let the browser and website or app exchange information about what **constrainable properties** the browser's implementation supports and what values it supports for each.

Overview
--------

The process works like this (using `MediaStreamTrack` as an example):

1. If needed, call `MediaDevices.getSupportedConstraints()` to get the list of **supported constraints**, which tells you what constrainable properties the browser knows about. This isn't always necessary, since any that aren't known will be ignored when you specify them—but if you have any that you can't get by without, you can start by checking to be sure they're on the list.
2. Once the script knows whether the property or properties it wishes to use are supported, it can then check the **capabilities** of the API and its implementation by examining the object returned by the track's `getCapabilities()` method; this object lists each supported constraint and the values or range of values which are supported.
3. Finally, the track's `applyConstraints()` method is called to configure the API as desired by specifying the values or ranges of values it wishes to use for any of the constrainable properties about which it has a preference.
4. The track's `getConstraints()` method returns the set of constraints passed into the most recent call to `applyConstraints()`. This may not represent the actual current state of the track, due to properties whose requested values had to be adjusted and because platform default values aren't represented. For a complete representation of the track's current configuration, use `getSettings()`.

In the Media Capture and Streams API, both `MediaStream` and `MediaStreamTrack` have constrainable properties.

Determining if a constraint is supported
----------------------------------------

If you need to know whether or not a given constraint is supported by the user agent, you can find out by calling `navigator.mediaDevices.getSupportedConstraints()` to get a list of the constrainable properties which the browser knows, like this:

```js
const supported = navigator.mediaDevices.getSupportedConstraints();

document.getElementById("frameRateSlider").disabled = !supported["frameRate"];
```

In this example, the supported constraints are fetched, and a control that lets the user configure the frame rate is disabled if the `frameRate` constraint isn't supported.

How constraints are defined
---------------------------

A single constraint is an object whose name matches the constrainable property whose desired value or range of values is being specified. This object contains zero or more individual constraints, as well as an optional sub-object named `advanced`, which contains another set of zero or more constraints which the user agent must satisfy if at all possible. The user agent attempts to satisfy constraints in the order specified in the constraint set.

The most important thing to understand is that most constraints aren't requirements; instead, they're requests. There are exceptions, and we'll get to those shortly.

### Requesting a specific value for a setting

Most, each constraint may be a specific value indicating a desired value for the setting. For example:

```js
const constraints = {
  width: 1920,
  height: 1080,
  aspectRatio: 1.777777778,
};

myTrack.applyConstraints(constraints);
```

In this case, the constraints indicate that any values are fine for nearly all properties, but that a standard high definition (HD) video size is desired, with the standard 16:9 aspect ratio. There's no guarantee that the resulting track will match any of these, but the user agent should do its best to match as many as possible.

The prioritization of the properties is simple: if two properties' requested values are mutually exclusive, then the one listed first in the constraint set will be used. As an example, if the browser running the code above couldn't provide a 1920x1080 track but could do 1920x900, then that's what would be provided.

Simple constraints like these, specifying a single value, are always treated as non-required. The user agent will try to provide what you request but will not guarantee that what you get will match. However, if you use simple values for properties when calling `MediaStreamTrack.applyConstraints()`, the request will always succeed, because these values will be considered a request, not a requirement.

### Specifying a range of values

Sometimes, any value within a range is acceptable for a property's value. You can specify ranges with either or both minimum and maximum values, and you can even specify an ideal value within the range, if you choose. If you provide an ideal value, the browser will try to get as close as possible to matching that value, given the other constraints specified.

```js
const supports = navigator.mediaDevices.getSupportedConstraints();

if (
  !supports["width"] ||
  !supports["height"] ||
  !supports["frameRate"] ||
  !supports["facingMode"]
) {
  // We're missing needed properties, so handle that error.
} else {
  const constraints = {
    width: { min: 640, ideal: 1920, max: 1920 },
    height: { min: 400, ideal: 1080 },
    aspectRatio: 1.777777778,
    frameRate: { max: 30 },
    facingMode: { exact: "user" },
  };

  myTrack
    .applyConstraints(constraints)
    .then(() => {
      /\* do stuff if constraints applied successfully \*/
    })
    .catch((reason) => {
      /\* failed to apply constraints; reason is why \*/
    });
}
```

Here, after ensuring that the constrainable properties for which matches must be found are supported (`width`, `height`, `frameRate`, and `facingMode`), we set up constraints which request a width no smaller than 640 and no larger than 1920 (but preferably 1920), a height no smaller than 400 (but ideally 1080), an aspect ratio of 16:9 (1.777777778), and a frame rate no greater than 30 frames per second. In addition, the only acceptable input device is a camera facing the user (a "selfie cam"). If the `width`, `height`, `frameRate`, or `facingMode` constraints can't be met, the promise returned by `applyConstraints()` will be rejected.

**Note:** Constraints which are specified using any or all of `max`, `min`, or `exact` are always treated as mandatory. If any constraint which uses one or more of those can't be met when calling `applyConstraints()`, the promise will be rejected.

### Advanced constraints

So-called advanced constraints are created by adding an `advanced` property to the constraint set; this property's value is an array of additional constraint sets which are considered optional. There are few if any use cases for this feature, and there is some interest in removing it from the specification, so it will not be discussed here. If you wish to learn more, see section 11 of the Media Capture and Streams specification, past example 2.

Checking capabilities
---------------------

You can call `MediaStreamTrack.getCapabilities()` to get a list of all of the supported capabilities and the values or ranges of values which each one accepts on the current platform and user agent. This function returns an object which lists each constrainable property supported by the browser and a value or range of values which are supported for each one of those properties.

**Note:** `getCapabilities()` hasn't been implemented yet by all major browsers. For the time being, you'll have to try to get what you need, and if you can't, decide what to do at that point. See Firefox Firefox bug 1179084, for example.

Applying constraints
--------------------

The first and most common way to use constraints is to specify them when you call `getUserMedia()`:

```js
navigator.mediaDevices
  .getUserMedia({
    video: {
      width: { min: 640, ideal: 1920 },
      height: { min: 400, ideal: 1080 },
      aspectRatio: { ideal: 1.7777777778 },
    },
    audio: {
      sampleSize: 16,
      channelCount: 2,
    },
  })
  .then((stream) => {
    videoElement.srcObject = stream;
  })
  .catch(handleError);
```

In this example, constraints are applied at `getUserMedia()` time, asking for an ideal set of options with fallbacks for the video.

**Note:** You can specify one or more media input device IDs to establish restrictions on which input sources are allowed. To collect a list of the available devices, you can call `navigator.mediaDevices.enumerateDevices()`, then for each device which meets the desired criteria, add its `deviceId` to the `MediaConstraints` object that eventually gets passed into `getUserMedia()`.

You can also change the constraints of an existing `MediaStreamTrack` on the fly, by calling the track's `applyConstraints()` method, passing into it an object representing the constraints you wish to apply to the track:

```js
videoTrack.applyConstraints({
  width: 1920,
  height: 1080,
});
```

In this snippet, the video track referenced by `videoTrack` is updated so that its resolution as closely as possible matches 1920x1080 pixels (1080p high definition).

Retrieving current constraints and settings
-------------------------------------------

It's important to remember the difference between **constraints** and **settings**. Constraints are a way to specify what values you need, want, and are willing to accept for the various constrainable properties (as described in the documentation for `MediaTrackConstraints`), while settings are the actual values of each constrainable property at the current time.

### Getting the constraints in effect

If at any time you need to fetch the set of constraints that are currently applied to the media, you can get that information by calling `MediaStreamTrack.getConstraints()`, as shown in the example below.

```js
function switchCameras(track, camera) {
  const constraints = track.getConstraints();
  constraints.facingMode = camera;
  track.applyConstraints(constraints);
}
```

This function accepts a `MediaStreamTrack` and a string indicating the camera facing mode to use, fetches the current constraints, sets the value of the `MediaTrackConstraints.facingMode` to the specified value, then applies the updated constraint set.

### Getting the current settings for a track

Unless you only use exact constraints (which is pretty restrictive, so be sure you mean it!), there's no guarantee exactly what you're going to actually get after the constraints are applied. The values of the constrainable properties as they actually are in the resulting media are referred to as the settings. If you need to know the true format and other properties of the media, you can obtain those settings by calling `MediaStreamTrack.getSettings()`. This returns an object based on the dictionary `MediaTrackSettings`. For example:

```js
function whichCamera(track) {
  return track.getSettings().facingMode;
}
```

This function uses `getSettings()` to obtain the track's currently in-use values for the constrainable properties and returns the value of `facingMode`.

Example: Constraint exerciser
-----------------------------

In this example, we create an exerciser which lets you experiment with media constraints by editing the source code describing the constraint sets for audio and video tracks. You can then apply those changes and see the result, including both what the stream looks like and what the actual media settings are set to after applying the new constraints.

The HTML and CSS for this example are pretty simple, and aren't shown here. You can look at the complete example by clicking here.
```

<p>
  Experiment with media constraints! Edit the constraint sets for the video and
  audio tracks in the edit boxes on the left, then click the "Apply Constraints"
  button to try them out. The actual settings the browser selected and is using
  are shown in the boxes on the right. Below all of that, you'll see the video
  itself.
</p>
<p>Click the "Start" button to begin.</p>

<h3>Constrainable properties available:</h3>
<ul id="supportedConstraints"></ul>
<div id="startButton" class="button">Start</div>
<div class="wrapper">
  <div class="trackrow">
    <div class="leftside">
      <h3>Requested video constraints:</h3>
      <textarea id="videoConstraintEditor" cols="32" rows="8"></textarea>
    </div>
    <div class="rightside">
      <h3>Actual video settings:</h3>
      <textarea id="videoSettingsText" cols="32" rows="8" disabled></textarea>
    </div>
  </div>
  <div class="trackrow">
    <div class="leftside">
      <h3>Requested audio constraints:</h3>
      <textarea id="audioConstraintEditor" cols="32" rows="8"></textarea>
    </div>
    <div class="rightside">
      <h3>Actual audio settings:</h3>
      <textarea id="audioSettingsText" cols="32" rows="8" disabled></textarea>
    </div>
  </div>

  <div class="button" id="applyButton">Apply Constraints</div>
</div>
<video id="video" autoplay></video>

<div class="button" id="stopButton">Stop Video</div>

<div id="log"></div>
```

```
body {
  font:
    14px "Open Sans",
    "Arial",
    sans-serif;
}

video {
  margin-top: 20px;
  border: 1px solid black;
}

.button {
  cursor: pointer;
  width: 150px;
  border: 1px solid black;
  font-size: 16px;
  text-align: center;
  padding-top: 2px;
  padding-bottom: 4px;
  color: white;
  background-color: darkgreen;
}

.wrapper {
  margin-bottom: 10px;
  width: 600px;
}

.trackrow {
  height: 200px;
}

.leftside {
  float: left;
  width: calc(calc(100% / 2) - 10px);
}

.rightside {
  float: right;
  width: calc(calc(100% / 2) - 10px);
}

textarea {
  padding: 8px;
}

h3 {
  margin-bottom: 3px;
}

#supportedConstraints {
  column-count: 2;
}

#log {
  padding-top: 10px;
}
```

### Defaults and variables

First we have the default constraint sets, as strings. These strings are presented in editable `<textarea>`s, but this is the initial configuration of the stream.

```js
const videoDefaultConstraintString =
  '{\n "width": 320,\n "height": 240,\n "frameRate": 30\n}';
const audioDefaultConstraintString =
  '{\n "sampleSize": 16,\n "channelCount": 2,\n "echoCancellation": false\n}';
```

These defaults ask for a pretty common camera configuration, but don't insist on any property being of special importance. The browser should do its best to match these settings but will settle for anything it considers a close match.

Then we initialize the variables which will hold the `MediaTrackConstraints` objects for the video and audio tracks, as well as the variables which will hold references to the video and audio tracks themselves, to `null`.

```js
let videoConstraints = null;
let audioConstraints = null;

let audioTrack = null;
let videoTrack = null;
```

And we get references to all of the elements we'll need to access.

```js
const videoElement = document.getElementById("video");
const logElement = document.getElementById("log");
const supportedConstraintList = document.getElementById("supportedConstraints");
const videoConstraintEditor = document.getElementById("videoConstraintEditor");
const audioConstraintEditor = document.getElementById("audioConstraintEditor");
const videoSettingsText = document.getElementById("videoSettingsText");
const audioSettingsText = document.getElementById("audioSettingsText");
```

These elements are:

`videoElement`

The `<video>` element that will show the stream.

`logElement`

A `<div>` into which any error messages or other log-type output will be written.

`supportedConstraintList`

A `<ul>` (unordered list) into which we programmatically add the names of each of the constrainable properties supported by the user's browser.

`videoConstraintEditor`

A `<textarea>` element that lets the user edit the code for the video track's constraint set.

`audioConstraintEditor`

A `<textarea>` element that lets the user edit the code for the audio track's constraint set.

`videoSettingsText`

A `<textarea>` (which is always disabled) that displays the current settings for the video track's constrainable properties.

`audioSettingsText`

A `<textarea>` (which is always disabled) that displays the current settings for the audio track's constrainable properties.

Finally, we set the current contents of the two constraint set editor elements to the defaults.

```js
videoConstraintEditor.value = videoDefaultConstraintString;
audioConstraintEditor.value = audioDefaultConstraintString;
```

### Updating the settings display

To the right of each of the constraint set editors is a second text box which we use to display the current configuration of the track's configurable properties. This display is updated by the function `getCurrentSettings()`, which gets the current settings for the audio and video tracks and inserts the corresponding code into the tracks' settings display boxes by setting their `value`.

```js
function getCurrentSettings() {
  if (videoTrack) {
    videoSettingsText.value = JSON.stringify(videoTrack.getSettings(), null, 2);
  }

  if (audioTrack) {
    audioSettingsText.value = JSON.stringify(audioTrack.getSettings(), null, 2);
  }
}
```

This gets called after the stream first starts up, as well as any time we've applied updated constraints, as you'll see below.

### Building the track constraint set objects

The `buildConstraints()` function builds the `MediaTrackConstraints` objects for the audio and video tracks using the code in the two tracks' constraint set edit boxes.

```js
function buildConstraints() {
  try {
    videoConstraints = JSON.parse(videoConstraintEditor.value);
    audioConstraints = JSON.parse(audioConstraintEditor.value);
  } catch (error) {
    handleError(error);
  }
}
```

This uses `JSON.parse()` to parse the code in each editor into an object. If either call to JSON.parse() throws an exception, `handleError()` is called to output the error message to the log.

### Configuring and starting the stream

The `startVideo()` method handles setting up and starting the video stream.

```js
function startVideo() {
  buildConstraints();

  navigator.mediaDevices
    .getUserMedia({
      video: videoConstraints,
      audio: audioConstraints,
    })
    .then((stream) => {
      const audioTracks = stream.getAudioTracks();
      const videoTracks = stream.getVideoTracks();

      videoElement.srcObject = stream;

      if (audioTracks.length > 0) {
        audioTrack = audioTracks[0];
      }

      if (videoTracks.length > 0) {
        videoTrack = videoTracks[0];
      }
    })
    .then(() => {
      return new Promise((resolve) => {
        videoElement.onloadedmetadata = resolve;
      });
    })
    .then(() => {
      getCurrentSettings();
    })
    .catch(handleError);
}
```

There are several steps here:

1. It calls `buildConstraints()` to create the `MediaTrackConstraints` objects for the two tracks from the code in the edit boxes.
2. It calls `navigator.mediaDevices.getUserMedia()`, passing in the constraints objects for the video and audio tracks. This returns a `MediaStream` with the audio and video from a source matching the inputs (typically a webcam, although if you provide the right constraints you can get media from other sources).
3. When the stream is obtained, it's attached to the `<video>` element so that it's visible on screen, and we grab the audio track and video track into the variables `audioTrack` and `videoTrack`.
4. Then we set up a promise which resolves when the `loadedmetadata` event occurs on the video element.
5. When that happens, we know the video has started playing, so we call our `getCurrentSettings()` function (described above) to display the actual settings that the browser decided upon after considering our constraints and the capabilities of the hardware.
6. If an error occurs, we log it using the `handleError()` method that we'll look at farther down in this article.

We also need to set up an event listener to watch for the "Start Video" button to be clicked:

```js
document.getElementById("startButton").addEventListener(
  "click",
  () => {
    startVideo();
  },
  false,
);
```

### Applying constraint set updates

Next, we set up an event listener for the "Apply Constraints" button. If it's clicked and there's not already media in use, we call `startVideo()`, and let that function handle starting the stream with the specified settings in place. Otherwise, we follow these steps to apply the updated constraints to the already-active stream:

1. `buildConstraints()` is called to construct updated `MediaTrackConstraints` objects for the audio track (`audioConstraints`) and the video track (`videoConstraints`).
2. `MediaStreamTrack.applyConstraints()` is called on the video track (if there is one) to apply the new `videoConstraints`. If this succeeds, the contents of the video track's current settings box are updated based on the result of calling its `getSettings()` method.
3. Once that's done, `applyConstraints()` is called on the audio track (if there is one) to apply the new audio constraints. If this succeeds, the contents of the audio track's current settings box are updated based on the result of calling its `getSettings()` method.
4. If an error occurs applying either set of constraints, `handleError()` is used to output a message into the log.

```js
document.getElementById("applyButton").addEventListener(
  "click",
  () => {
    if (!videoTrack && !audioTrack) {
      startVideo();
    } else {
      buildConstraints();

      const prettyJson = (obj) => JSON.stringify(obj, null, 2);

      if (videoTrack) {
        videoTrack
          .applyConstraints(videoConstraints)
          .then(() => {
            videoSettingsText.value = prettyJson(videoTrack.getSettings());
          })
          .catch(handleError);
      }

      if (audioTrack) {
        audioTrack
          .applyConstraints(audioConstraints)
          .then(() => {
            audioSettingsText.value = prettyJson(audioTrack.getSettings());
          })
          .catch(handleError);
      }
    }
  },
  false,
);
```

### Handling the stop button

Then we set up the handler for the stop button.

```js
document.getElementById("stopButton").addEventListener("click", () => {
  if (videoTrack) {
    videoTrack.stop();
  }

  if (audioTrack) {
    audioTrack.stop();
  }

  videoTrack = audioTrack = null;
  videoElement.srcObject = null;
});
```

This stops the active tracks, sets the `videoTrack` and `audioTrack` variables to `null` so we know they're gone, and removes the stream from the `<video>` element by setting `HTMLMediaElement.srcObject` to `null`.

### Simple tab support in the editor

This code adds simple support for tabs to the `<textarea>` elements by making the tab key insert two space characters when either constraint edit box is focused.

```js
function keyDownHandler(event) {
  if (event.key === "Tab") {
    const elem = event.target;
    const str = elem.value;

    const position = elem.selectionStart;
    const beforeTab = str.substring(0, position);
    const afterTab = str.substring(position, str.length);
    const newStr = `${beforeTab} ${afterTab}`;
    elem.value = newStr;
    elem.selectionStart = elem.selectionEnd = position + 2;
    event.preventDefault();
  }
}

videoConstraintEditor.addEventListener("keydown", keyDownHandler, false);
audioConstraintEditor.addEventListener("keydown", keyDownHandler, false);
```

### Show constrainable properties the browser supports

The last significant piece of the puzzle: code that displays, for the user's reference, a list of the constrainable properties which their browser supports. Each property is a link to its documentation on MDN for the user's convenience. See the `MediaDevices.getSupportedConstraints()` examples for details on how this code works.

**Note:** Of course, there may be non-standard properties in this list, in which case you probably will find that the documentation link doesn't help much.

```js
const supportedConstraints = navigator.mediaDevices.getSupportedConstraints();
for (const constraint in supportedConstraints) {
  if (Object.hasOwn(supportedConstraints, constraint)) {
    const elem = document.createElement("li");

    elem.innerHTML = `<code><a href='https://developer.mozilla.org/docs/Web/API/MediaTrackSupportedConstraints/${constraint}' target='\_blank'>${constraint}</a></code>`;
    supportedConstraintList.appendChild(elem);
  }
}
```

### Error handling

We also have some simple error handling code; `handleError()` is called to handle promises which fail, and the `log()` function appends the error message to a special logging `<div>` box under the video.

```js
function log(msg) {
  logElement.innerHTML += `${msg}<br>`;
}

function handleError(reason) {
  log(
    `Error <code>${reason.name}</code> in constraint <code>${reason.constraint}</code>: ${reason.message}`,
  );
}
```

### Result

Here you can see the complete example in action.

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

| Specification |
| --- |
| Media Capture and Streams # dom-mediadevices-getsupportedconstraints |

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

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

See also
--------

* Media Capture and Streams API
* `MediaTrackConstraints`
* `MediaTrackSettings`
* `MediaDevices.getSupportedConstraints()`
* `MediaStreamTrack.applyConstraints()`
* `MediaStreamTrack.getSettings()`


Scheduler: postTask() method - Web APIs

Scheduler: postTask() method
============================

Limited availability
--------------------

This feature is not Baseline because it does not work in some of the most widely-used browsers.

* Learn more
* See full compatibility
* Report feedback
The **`postTask()`** method of the `Scheduler` interface is used for adding tasks to be scheduled according to their priority.

 The method allows users to optionally specify a minimum delay before the task will run, a priority for the task, and a signal that can be used to modify the task priority and/or abort the task.
 It returns a promise that is resolved with the result of the task callback function, or rejected with the abort reason or an error thrown in the task.

 Task priority can be mutable or immutable.
 If the task priority will never need to change then it should be set using the `options.priority` parameter (any priority set through a signal will then be ignored).
 You can still pass an `AbortSignal` (which has no priority) or `TaskSignal` to the `options.signal` parameter for aborting the task.

 If the task priority might need to be changed the `options.priority` parameter must not be set.
 Instead a `TaskController` should be created and its `TaskSignal` should be passed to `options.signal`.
 The task priority will be initialized from the signal priority, and can later be modified using the signal's associated `TaskController`.

If no priority is set then the task priority defaults to `"user-visible"`.

 If a delay is specified and greater than 0, then the execution of the task will be delayed for at least that many milliseconds.
 Otherwise the task is immediately scheduled for prioritization.

Syntax
------

```js
postTask(callback)
postTask(callback, options)
```

### Parameters

`callback`

 An callback function that implements the task.
 The return value of the callback is used to resolve the promise returned by this function.

`options` Optional

Task options, including:

`priority` Optional

 The immutable priority of the task.
 One of: `"user-blocking"`, `"user-visible"`, `"background"`.
 If set, this priority is used for the lifetime of the task and priority set on the `signal` is ignored.

`signal` Optional

A `TaskSignal` or `AbortSignal` that can be used to abort the task (from its associated controller).

 If the `options.priority` parameter is set then the task priority cannot be changed, and any priority on the signal is ignored.
 Otherwise, if the signal is a `TaskSignal` its priority is used to set the initial task priority, and the signal's controller may later use it to change the task priority.

`delay` Optional

 The minimum amount of time after which the task will be added to the scheduler queue, in whole milliseconds.
 The actual delay may be higher than specified, but will not be less.
 The default delay is 0.

### Return Value

 Returns a `Promise` that is resolved with the return value of the `callback` function, or which may be rejected with the `signal`'s abort reason (`AbortSignal.reason`).
 The promise may also be rejected with an error thrown by the callback during execution.

Examples
--------

The following examples are slightly simplified versions of the live examples provided in Prioritized Task Scheduling API > Examples.

### Feature checking

Check whether prioritized task scheduling is supported by testing for the `scheduler` property in the global "`this`" (such as `Window.scheduler`).

For example, the code below logs "Feature: Supported" if the API is supported on this browser.

```js
// Check that feature is supported
if ("scheduler" in this) {
  console.log("Feature: Supported");
} else {
  console.error("Feature: NOT Supported");
}
```

### Basic usage

 Tasks are posted specifying a callback function (task) in the first argument, and an optional second argument that can be used to specify a task priority, signal, and/or delay.
 The method returns a `Promise` that resolves with the return value of the callback function, or rejects with either an abort error or an error thrown in the function.

 Because it returns a promise, `postTask()` can be chained with other promises.
 Below we show how to wait on the promise to resolve using `then` or reject using `catch`.
 The priority is not specified, so the default priority of `user-visible` will be used.

```js
// A function that defines a task
function myTask() {
  return "Task 1: user-visible";
}

// Post task with default priority: 'user-visible' (no other options)
// When the task resolves, Promise.then() logs the result.
scheduler
  .postTask(myTask, { signal: abortTaskController.signal })
  .then((taskResult) => console.log(`${taskResult}`)) // Log resolved value
  .catch((error) => console.error("Error:", error)); // Log error or abort
```

 The method can also be used with `await` inside an async function.
 The code below shows how you might use this approach to wait on a `user-blocking` task.

```js
function myTask2() {
  return "Task 2: user-blocking";
}

async function runTask2() {
  const result = await scheduler.postTask(myTask2, {
    priority: "user-blocking",
  });
  console.log(result); // 'Task 2: user-blocking'.
}
runTask2();
```

### Permanent priorities

Task priorities may be set using `priority` parameter in the optional second argument.
 Priorities that are set in this way cannot be changed (are immutable).

 Below we post two groups of three tasks, each member in reverse order of priority.
 The final task has the default priority.
 When run, each task simply logs it's expected order (we're not waiting on the result because we don't need to in order to show execution order).

```js
// three tasks, in reverse order of priority
scheduler.postTask(() => console.log("bckg 1"), { priority: "background" });
scheduler.postTask(() => console.log("usr-vis 1"), {
  priority: "user-visible",
});
scheduler.postTask(() => console.log("usr-blk 1"), {
  priority: "user-blocking",
});

// three more tasks, in reverse order of priority
scheduler.postTask(() => console.log("bckg 2"), { priority: "background" });
scheduler.postTask(() => console.log("usr-vis 2"), {
  priority: "user-visible",
});
scheduler.postTask(() => console.log("usr-blk 2"), {
  priority: "user-blocking",
});

// Task with default priority: user-visible
scheduler.postTask(() => {
  console.log("usr-vis 3 (default)");
});
```

The expected output is shown below: tasks are executed in priority order, and then declaration order.
```

usr-blk 1
usr-blk 2
usr-vis 1
usr-vis 2
usr-vis 3 (default)
bckg 1
bckg 2
```

### Changing task priorities

Task priorities can also take their initial value from a `TaskSignal` passed to `postTask()` in the optional second argument.
 If set in this way, the priority of the task can then be changed using the controller associated with the signal.

**Note:** Setting and changing task priorities using a signal only works when the `options.priority` argument to `postTask()` is not set, and when the `options.signal` is a `TaskSignal` (and not an `AbortSignal`).

The code below first shows how to create a `TaskController`, setting the initial priority of its signal to `user-blocking` in the `TaskController()` constructor.

 We then use `addEventListener()` to add an event listener to the controller's signal (we could alternatively use the `TaskSignal.onprioritychange` property to add an event handler).
 The event handler uses `previousPriority` on the event to get the original priority and `TaskSignal.priority` on the event target to get the new/current priority.

```js
// Create a TaskController, setting its signal priority to 'user-blocking'
const controller = new TaskController({ priority: "user-blocking" });

// Listen for 'prioritychange' events on the controller's signal.
controller.signal.addEventListener("prioritychange", (event) => {
  const previousPriority = event.previousPriority;
  const newPriority = event.target.priority;
  console.log(`Priority changed from ${previousPriority} to ${newPriority}.`);
});
```

Finally, the task is posted, passing in the signal, and then we immediately change the priority to `background` by calling `TaskController.setPriority()` on the controller.

```js
// Post task using the controller's signal.
// The signal priority sets the initial priority of the task
scheduler.postTask(() => console.log("Task 1"), { signal: controller.signal });

// Change the priority to 'background' using the controller
controller.setPriority("background");
```

 The expected output is shown below.
 Note that in this case the priority is changed before the task is executed, but it could equally have been changed while the task was running.

```js
// Expected output
// Priority changed from user-blocking to background.
// Task 1
```

### Aborting tasks

 Tasks can be aborted using either `TaskController` and `AbortController`, in exactly the same way.
 The only difference is that you must use `TaskController` if you also want to set the task priority.

 The code below creates a controller and passes its signal to the task.
 The task is then immediately aborted.
 This causes the promise to be rejected with an `AbortError`, which is caught in the `catch` block and logged.
 Note that we could also have listened for the `abort` event fired on the `TaskSignal` or `AbortSignal` and logged the abort there.

```js
// Declare a TaskController with default priority
const abortTaskController = new TaskController();
// Post task passing the controller's signal
scheduler
  .postTask(() => console.log("Task executing"), {
    signal: abortTaskController.signal,
  })
  .then((taskResult) => console.log(`${taskResult}`)) //This won't run!
  .catch((error) => console.error("Error:", error)); // Log the error

// Abort the task
abortTaskController.abort();
```

### Delaying tasks

 Tasks can be delayed by specifying an integer number of milliseconds in the `options.delay` parameter to `postTask()`.
 This effectively adds the task to the prioritized queue on a timeout, as might be created using `setTimeout()`.
 The `delay` is the minimum amount of time before the task is added to the scheduler; it may be longer.

The code below shows two tasks added (as arrow functions) with a delay.

```js
// Post task as arrow function with delay of 2 seconds
scheduler
  .postTask(() => "Task delayed by 2000ms", { delay: 2000 })
  .then((taskResult) => console.log(`${taskResult}`));
scheduler
  .postTask(() => "Next task should complete in about 2000ms", { delay: 1 })
  .then((taskResult) => console.log(`${taskResult}`));
```

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

| Specification |
| --- |
| Prioritized Task Scheduling # dom-scheduler-posttask |

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

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


GeolocationPosition: timestamp property - Web APIs

GeolocationPosition: timestamp property
=======================================

**Secure context:** This feature is available only in secure contexts (HTTPS), in some or all supporting browsers.

The **`timestamp`** read-only property of the `GeolocationPosition` interface represents the date and time that the position was acquired by the device.

Value
-----

A number containing a timestamp, given as Unix time in milliseconds.

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

| Specification |
| --- |
| Geolocation API # timestamp-attribute |

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

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

See also
--------

* Using the Geolocation API
* `GeolocationPosition`


GeolocationPosition: coords property - Web APIs

GeolocationPosition: coords property
====================================

**Secure context:** This feature is available only in secure contexts (HTTPS), in some or all supporting browsers.

The **`coords`** read-only property of the `GeolocationPosition` interface returns a `GeolocationCoordinates` object representing a geographic position. It contains the location, that is longitude and latitude on the Earth, the altitude, and the speed of the object concerned, regrouped inside the returned value. It also contains accuracy information about these values.

Value
-----

A `GeolocationCoordinates` object instance.

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

| Specification |
| --- |
| Geolocation API # coords-attribute |

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

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

See also
--------

* Using the Geolocation API
* `GeolocationPosition`


HTMLAudioElement: Audio() constructor - Web APIs

HTMLAudioElement: Audio() constructor
=====================================

 The **`Audio()`** constructor creates
 and returns a new `HTMLAudioElement` which can be either attached to
 a document for the user to interact with and/or listen to, or can be used
 offscreen to manage and play audio.

Syntax
------

```js
new Audio()
new Audio(url)
```

### Parameters

`url` Optional

 An optional string containing the URL of an audio file to be
 associated with the new audio element.

### Return value

 A new `HTMLAudioElement` object, configured to be used for playing back
 the audio from the file specified by `url`. The new object's
 `preload` property is set
 to `auto` and its `src` property is set to the specified URL
 or `null` if no URL is given. If a URL is specified, the browser begins
 to *asynchronously* load the media resource before returning the new object.

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

 You can also use other element-creation methods, such as the `document`
 object's `createElement()` method, to construct
 a new `HTMLAudioElement`.

### Determining when playback can begin

 There are three ways you can tell when enough of the audio file has loaded to allow
 playback to begin:

* Check the value of the `readyState`
 property. If it's `HTMLMediaElement.HAVE_FUTURE_DATA`, there's enough
 data available to begin playback and play for at least a short time. If
 it's `HTMLMediaElement.HAVE_ENOUGH_DATA`, then there's enough data
 available that, given the current download rate, you should be able to play the
 audio through to the end without interruption.
* Listen for the `canplay` event. It
 is sent to the `<audio>` element when there's enough audio
 available to begin playback, although interruptions may occur.
* Listen for the `canplaythrough` event. It is sent when it's estimated that the audio should be
 able to play to the end without interruption.

The event-based approach is best:

```js
myAudioElement.addEventListener("canplaythrough", (event) => {
  /\* the audio is now playable; play it if permissions allow \*/
  myAudioElement.play();
});
```

### Memory usage and management

 If all references to an audio element created using
 the `Audio()` constructor are deleted, the element itself won't be removed
 from memory by the JavaScript runtime's garbage collection mechanism if playback is
 currently underway. Instead, the audio will keep playing and the object will remain in
 memory until playback ends. At that time, the object becomes
 subject to garbage collection.

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

| Specification |
| --- |
| HTML Standard # dom-audio-dev |

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

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

See also
--------

* Web media technologies
* HTML element implementing this interface: `<audio>`.


SVGAnimationElement: repeatEvent event - Web APIs

SVGAnimationElement: repeatEvent event
======================================

The **`repeatEvent`** event of the `SVGAnimationElement` interface is fired when the element's local timeline repeats. It will be fired each time the element repeats, after the first iteration.

**Note:** Associated with the `repeatEvent` event is an integer that indicates which repeat iteration is beginning; this can be found in the `detail` property of the event object. The value is a 0-based integer, but the repeat event is not raised for the first iteration and so the observed values will be >= 1. This is supported in Firefox, but not in Chrome.

This event is not cancelable and does not bubble.

Syntax
------

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

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

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

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

A `TimeEvent`. Inherits from `Event`.

Event

TimeEventEvent properties
----------------

`TimeEvent.detail` Read only

A `long` that specifies some detail information about the Event, depending on the type of the event. For this event type, indicates the repeat number for the animation.

`TimeEvent.view` Read only

A WindowProxy that identifies the Window from which the event was generated.

Examples
--------

### Animated circle

```html
<svg xmlns="http://www.w3.org/2000/svg" width="300px" height="100px">
  <title>SVG SMIL Animate with Path</title>
  <circle cx="0" cy="50" r="50" fill="blue" stroke="black" stroke-width="1">
    <animateMotion path="M 0 0 H 300 Z" dur="5s" repeatCount="indefinite" />
  </circle>
</svg>

<hr />

<ul></ul>
```

```css
ul {
  height: 100px;
  border: 1px solid #ddd;
  overflow-y: scroll;
  padding: 10px 30px;
}
```

```js
let svgElem = document.querySelector("svg");
let animateElem = document.querySelector("animateMotion");
let list = document.querySelector("ul");

animateElem.addEventListener("beginEvent", () => {
  let listItem = document.createElement("li");
  listItem.textContent = "beginEvent fired";
  list.appendChild(listItem);
});

animateElem.addEventListener("repeatEvent", (e) => {
  let listItem = document.createElement("li");
  let msg = "repeatEvent fired";
  if (e.detail) {
    msg += `; repeat number: ${e.detail}`;
  }
  listItem.textContent = msg;
  list.appendChild(listItem);
});
```

### Event handler property equivalent

Note that you can also create an event listener for the `repeat` event using the `onrepeat` event handler property:

```js
animateElem.onrepeat = () => {
  console.log("repeatEvent fired");
};
```

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

| Specification |
| --- |
| Scalable Vector Graphics (SVG) 2 # RepeatEvent |

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

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

See also
--------

* SVG animation with SMIL
* `beginEvent` event
* `endEvent` event


SVGAnimationElement: targetElement property - Web APIs

SVGAnimationElement: targetElement property
===========================================

 The **`SVGAnimationElement.targetElement`** property refers to
 the element which is being animated. If no target element is being animated (for
 example, because the `href` attribute specifies an unknown element), the
 value returned is `null`.

Value
-----

A SVGElement object.

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

| Specification |
| --- |
| SVG Animations Level 2 # \_\_svg\_\_SVGAnimationElement\_\_targetElement |

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

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


SVGAnimationElement: endEvent event - Web APIs

SVGAnimationElement: endEvent event
===================================

The **`endEvent`** event of the `SVGAnimationElement` interface is fired when at the active end of the animation is reached.

**Note:** This event is not raised at the simple end of each animation repeat. This event may be raised both in the course of normal (i.e. scheduled or interactive) timeline play, as well as in the case that the element was ended with a DOM method.

This event is not cancelable and does not bubble.

Syntax
------

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

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

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

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

A `TimeEvent`. Inherits from `Event`.

Event

TimeEventEvent properties
----------------

`TimeEvent.detail` Read only

A `long` that specifies some detail information about the Event, depending on the type of the event. For this event type, indicates the repeat number for the animation.

`TimeEvent.view` Read only

A WindowProxy that identifies the Window from which the event was generated.

Examples
--------

### Animated circle

```html
<svg xmlns="http://www.w3.org/2000/svg" width="300px" height="100px">
  <title>SVG SMIL Animate with Path</title>
  <circle cx="0" cy="50" r="50" fill="blue" stroke="black" stroke-width="1">
    <animateMotion path="M 0 0 H 300 Z" dur="5s" repeatCount="indefinite" />
  </circle>
</svg>

<hr />

<button>Stop animation</button>

<ul></ul>
```

```css
ul {
  height: 100px;
  border: 1px solid #ddd;
  overflow-y: scroll;
  padding: 10px 30px;
}
```

```js
let svgElem = document.querySelector("svg");
let animateElem = document.querySelector("animateMotion");
let list = document.querySelector("ul");
let btn = document.querySelector("button");

animateElem.addEventListener("beginEvent", () => {
  let listItem = document.createElement("li");
  listItem.textContent = "beginEvent fired";
  list.appendChild(listItem);
});

animateElem.addEventListener("endEvent", () => {
  let listItem = document.createElement("li");
  listItem.textContent = "endEvent fired";
  list.appendChild(listItem);
});

animateElem.addEventListener("repeatEvent", (e) => {
  let listItem = document.createElement("li");
  let msg = "repeatEvent fired";
  if (e.detail) {
    msg += `; repeat number: ${e.detail}`;
  }
  listItem.textContent = msg;
  list.appendChild(listItem);
});

btn.addEventListener("click", () => {
  btn.disabled = true;
  animateElem.setAttribute("repeatCount", "1");
});
```

### Event handler property equivalent

Note that you can also create an event listener for the `end` event using the `onend` event handler property:

```js
animateElem.onend = () => {
  console.log("endEvent fired");
};
```

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

| Specification |
| --- |
| Scalable Vector Graphics (SVG) 2 # EndEvent |

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

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

See also
--------

* SVG animation with SMIL
* `beginEvent` event
* `repeatEvent` event


SVGAnimationElement: beginEvent event - Web APIs

SVGAnimationElement: beginEvent event
=====================================

The **`beginEvent`** event of the `SVGAnimationElement` interface is fired when the element local timeline begins to play. It will be raised each time the element begins the active duration (i.e., when it restarts, but not when it repeats).

It may be raised both in the course of normal (i.e. scheduled or interactive) timeline play, as well as in the case that the element was begun with a DOM method.

This event is not cancelable and does not bubble.

Syntax
------

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

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

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

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

A `TimeEvent`. Inherits from `Event`.

Event

TimeEventEvent properties
----------------

`TimeEvent.detail` Read only

A `long` that specifies some detail information about the Event, depending on the type of the event. For this event type, indicates the repeat number for the animation.

`TimeEvent.view` Read only

A WindowProxy that identifies the Window from which the event was generated.

Examples
--------

### Animated circle

```html
<svg xmlns="http://www.w3.org/2000/svg" width="300px" height="100px">
  <title>SVG SMIL Animate with Path</title>
  <circle cx="0" cy="50" r="50" fill="blue" stroke="black" stroke-width="1">
    <animateMotion path="M 0 0 H 300 Z" dur="5s" repeatCount="indefinite" />
  </circle>
</svg>

<hr />

<ul></ul>
```

```css
ul {
  height: 100px;
  border: 1px solid #ddd;
  overflow-y: scroll;
  padding: 10px 30px;
}
```

```js
let svgElem = document.querySelector("svg");
let animateElem = document.querySelector("animateMotion");
let list = document.querySelector("ul");

animateElem.addEventListener("beginEvent", () => {
  let listItem = document.createElement("li");
  listItem.textContent = "beginEvent fired";
  list.appendChild(listItem);
});

animateElem.addEventListener("repeatEvent", (e) => {
  let listItem = document.createElement("li");
  let msg = "repeatEvent fired";
  if (e.detail) {
    msg += `; repeat number: ${e.detail}`;
  }
  listItem.textContent = msg;
  list.appendChild(listItem);
});
```

### Event handler property equivalent

Note that you can also create an event listener for the `begin` event using the `onbegin` event handler property:

```js
animateElem.onbegin = () => {
  console.log("beginEvent fired");
};
```

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

| Specification |
| --- |
| Scalable Vector Graphics (SVG) 2 # BeginEvent |

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

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

See also
--------

* SVG animation with SMIL
* `endEvent` event
* `repeatEvent` event


PeriodicSyncEvent: tag property - Web APIs

PeriodicSyncEvent: tag property
===============================

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

 The **`tag`** read-only property of the
 `PeriodicSyncEvent` interface returns the developer-defined identifier for
 the `PeriodicSyncEvent`. This is specified when calling the
 `PeriodicSyncManager.register()` method of the
 `PeriodicSyncManager` interface. Multiple tags can be used by the web app
 to run different periodic tasks at different frequencies.

Value
-----

Returns a `String` of the defined identifier.

Examples
--------

 The following example demonstrates listening for a periodic sync event in the service
 worker, and accessing the `tag` property.

```js
self.addEventListener("periodicsync", (event) => {
  console.log(event.tag); // logs the events tag
});
```

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

| Specification |
| --- |
| Web Periodic Background Synchronization # dom-periodicsyncevent-tag |

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

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

See also
--------

* Richer offline experiences with the Periodic Background Sync API
* A Periodic Background Sync demo app


PeriodicSyncEvent: PeriodicSyncEvent() constructor - Web APIs

PeriodicSyncEvent: PeriodicSyncEvent() constructor
==================================================

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

 The **`PeriodicSyncEvent()`** constructor
 creates a new `PeriodicSyncEvent` object. This constructor is not typically
 used. The browser creates these objects itself and provides them to
 `onperiodicsync` callback.

Syntax
------

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

### Parameters

`type`

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

`options`

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

`tag`

The tag referencing the sync event.

### Return value

A new `PeriodicSyncEvent` object configured using the given inputs.

Examples
--------

 This example constructs a new `PeriodicSyncEvent` with the relevant
 associated tag.

```js
const psEvent = new ExtendableEvent("periodicsync", { tag: "unique-tag" });
```

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

| Specification |
| --- |
| Web Periodic Background Synchronization # dom-periodicsyncevent-periodicsyncevent |

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

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

See also
--------

* Richer offline experiences with the Periodic Background Sync API
* A Periodic Background Sync demo app


BeforeUnloadEvent: returnValue property - Web APIs

BeforeUnloadEvent: returnValue property
=======================================

**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.

 The **`returnValue`** property of the
 `BeforeUnloadEvent` interface, when set to a truthy value, triggers a browser-generated confirmation dialog asking users to confirm if they *really* want to leave the page when they try to close or reload it, or navigate somewhere else. This is intended to help prevent loss of unsaved data.

**Note:** `returnValue` is a legacy feature, and best practice is to trigger the dialog by invoking `Event.preventDefault()` on the `BeforeUnloadEvent` object, while also setting `returnValue` to support legacy cases. See the `beforeunload` event reference for detailed up-to-date guidance.

Value
-----

`returnValue` is initialized to an empty string (`""`) value.

Setting it to just about any truthy value will cause the dialog to be triggered on page close/reload, however note that it also requires sticky activation. In other words, the browser will only show the dialog if the frame or any embedded frame receives a user gesture or user interaction. If the user has never interacted with the page, then there is no user data to save, so no legitimate use case for the dialog.

**Note:** A generic browser-specified string is displayed in the dialog. This cannot be controlled by the webpage code.

Examples
--------

See the `beforeunload` event reference page for a best practice example.

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

| Specification |
| --- |
| HTML Standard # dom-beforeunloadevent-returnvalue |

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

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


Using the Document Picture-in-Picture API - Web APIs

Using the Document Picture-in-Picture API
=========================================

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

**Secure context:** This feature is available only in secure contexts (HTTPS), in some or all supporting browsers.

This guide provides a walkthrough of typical usage of the Document Picture-in-Picture API.

**Note:** You can see the featured demo in action at Document Picture-in-Picture API Example (see the full source code also).

Sample HTML
-----------

The following HTML sets up a basic video player.

```html
<div id="container">
  <p class="in-pip-message">
    Video player is currently in the separate Picture-in-Picture window.
  </p>
  <div id="player">
    <video
 src="assets/bigbuckbunny.mp4"
 id="video"
 controls
 width="320"></video>

    <div id="credits">
      <a href="https://peach.blender.org/download/" target="\_blank">
        Video by Blender </a
 >;
      <a href="https://peach.blender.org/about/" target="\_blank">
        licensed CC-BY 3.0
      </a>
    </div>

    <div id="controlbar">
      <p class="no-picture-in-picture">
        Document Picture-in-Picture API not available
      </p>

      <p></p>
    </div>
  </div>
</div>
```

Feature detection
-----------------

To check if the Document Picture-in-Picture API is supported, you can test whether `documentPictureInPicture` is available on `window`:

```js
if ("documentPictureInPicture" in window) {
  document.querySelector(".no-picture-in-picture").remove();

  const togglePipButton = document.createElement("button");
  togglePipButton.textContent = "Toggle Picture-in-Picture";
  togglePipButton.addEventListener("click", togglePictureInPicture, false);

  document.getElementById("controlbar").appendChild(togglePipButton);
}
```

If it is available, we remove the "Document Picture-in-Picture API not available" message and instead add a `<button>` element to open the video player in a Document Picture-in-Picture window.

Open a Picture-in-Picture window
--------------------------------

The following JavaScript calls `window.documentPictureInPicture.requestWindow()` to open a blank Picture-in-Picture window. The returned `Promise` fulfills with a Picture-in-Picture `Window` object. The video player is moved to that window using `Element.append()`, and we display the message informing the user that it has been moved.

The `width` and `height` options of `requestWindow()` set the Picture-in-Picture window to the desired size. Browsers may clamp the option values if they are too large or too small to fit a user-friendly window size.

```js
async function togglePictureInPicture() {
  // Returns null if no pip window is currently open
  if (!!window.documentPictureInPicture.window) {
    // Open a Picture-in-Picture window.
    const pipWindow = await documentPictureInPicture.requestWindow({
      width: videoPlayer.clientWidth,
      height: videoPlayer.clientHeight,
    });

    // ...

    // Move the player to the Picture-in-Picture window.
    pipWindow.document.body.append(videoPlayer);

    // Display a message to say it has been moved
    inPipMessage.style.display = "block";
  }
}
```

Copy style sheets to the Picture-in-Picture window
--------------------------------------------------

To copy all CSS style sheets from the originating window, loop through all style sheets explicitly linked into or embedded in the document (via `Document.styleSheets`) and append them to the Picture-in-Picture window. Note that this is a one-time copy.

```js
// ...

// Copy style sheets over from the initial document
// so that the player looks the same.
[...document.styleSheets].forEach((styleSheet) => {
  try {
    const cssRules = [...styleSheet.cssRules]
      .map((rule) => rule.cssText)
      .join("");
    const style = document.createElement("style");

    style.textContent = cssRules;
    pipWindow.document.head.appendChild(style);
  } catch (e) {
    const link = document.createElement("link");

    link.rel = "stylesheet";
    link.type = styleSheet.type;
    link.media = styleSheet.media;
    link.href = styleSheet.href;
    pipWindow.document.head.appendChild(link);
  }
});

// ...
```

Handle when the Picture-in-Picture window closes
------------------------------------------------

The code for toggling the Picture-in-Picture window closed again when the button is pressed a second time looks like this:

```js
inPipMessage.style.display = "none";
playerContainer.append(videoPlayer);
window.documentPictureInPicture.window.close();
```

Here we reverse the DOM changes — hiding the message and putting the video player back in the player container in the main app window. We also close the Picture-in-Picture window programmatically using the `Window.close()` method.

However, you also need to consider the case where the user closes the Picture-in-Picture window by pressing the browser supplied close (X) button on the window itself. You can handle this by detecting when the window closes using the `pagehide` event:

```js
pipWindow.addEventListener("pagehide", (event) => {
  inPipMessage.style.display = "none";
  playerContainer.append(videoPlayer);
});
```

Listen to when the website enters Picture-in-Picture
----------------------------------------------------

Listen to the `enter` event on the `DocumentPictureInPicture` instance to know when a Picture-in-Picture window is opened.

In our demo, we use the `enter` event to add a mute toggle button to the Picture-in-Picture window:

```js
documentPictureInPicture.addEventListener("enter", (event) => {
  const pipWindow = event.window;
  console.log("Video player has entered the pip window");

  const pipMuteButton = pipWindow.document.createElement("button");
  pipMuteButton.textContent = "Mute";
  pipMuteButton.addEventListener("click", () => {
    const pipVideo = pipWindow.document.querySelector("#video");
    if (!pipVideo.muted) {
      pipVideo.muted = true;
      pipMuteButton.textContent = "Unmute";
    } else {
      pipVideo.muted = false;
      pipMuteButton.textContent = "Mute";
    }
  });

  pipWindow.document.body.append(pipMuteButton);
});
```

**Note:** The `DocumentPictureInPictureEvent` event object contains a `window` property to access the Picture-in-Picture window.

Access elements and handle events
---------------------------------

You can access elements in the Picture-in-Picture window in several different ways:

* The `Window` instance returned by the `DocumentPictureInPicture.requestWindow()` method, as seen above.
* Via the `window` property of the `DocumentPictureInPictureEvent` event object (on the `enter` event), as seen above.
* Via the `DocumentPictureInPicture.window` property:

```js
const pipWindow = window.documentPictureInPicture.window;
if (pipWindow) {
  // Mute video playing in the Picture-in-Picture window.
  const pipVideo = pipWindow.document.querySelector("#video");
  pipVideo.muted = true;
}
```

Once you've got a reference to the Picture-in-Picture `window` instance, you can manipulate the DOM (for example creating buttons) and respond to user input events (such as `click`) as you would do normally in the regular browser window context.


NavigationTransition: navigationType property - Web APIs

NavigationTransition: navigationType property
=============================================

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

 The **`navigationType`** read-only property of the
 `NavigationTransition` interface returns the type of the ongoing navigation.

Value
-----

An enumerated value representing the type of ongoing navigation.

The possible values are:

* `push`: A new location is navigated to, causing a new entry to be pushed onto the history list.
* `reload`: The `Navigation.currentEntry` is reloaded.
* `replace`: The `Navigation.currentEntry` is replaced with a new history entry. This new entry will reuse the same `key`, but be assigned a different `id`.
* `traverse`: The browser navigates from one existing history entry to another existing history entry.
Examples
--------

```js
console.log(navigation.transition.navigationType);
```

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

| Specification |
| --- |
| HTML Standard # dom-navigationtransition-navigationtype-dev |

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

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

See also
--------

* Modern client-side routing: the Navigation API
* Navigation API explainer
* Domenic Denicola's Navigation API live demo


NavigationTransition: finished property - Web APIs

NavigationTransition: finished property
=======================================

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

 The **`finished`** read-only property of the
 `NavigationTransition` interface returns a `Promise` that fulfills at the same time the `navigatesuccess` event fires, or rejects at the same time the `navigateerror` event fires.

Value
-----

A `Promise` that resolves to `undefined`.

Examples
--------

```js
async function cleanupNavigation() {
  await navigation.transition.finished;
  // Navigation has completed successfully
  // Cleanup any ongoing monitoring
}
```

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

| Specification |
| --- |
| HTML Standard # dom-navigationtransition-finished-dev |

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

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

See also
--------

* Modern client-side routing: the Navigation API
* Navigation API explainer
* Domenic Denicola's Navigation API live demo


NavigationTransition: from property - Web APIs

NavigationTransition: from property
===================================

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

 The **`from`** read-only property of the
 `NavigationTransition` interface returns the `NavigationHistoryEntry` that the transition is coming from.

Value
-----

A `NavigationHistoryEntry` object.

Examples
--------

```js
console.log(navigation.transition.from);
```

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

| Specification |
| --- |
| HTML Standard # dom-navigationtransition-from-dev |

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

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

See also
--------

* Modern client-side routing: the Navigation API
* Navigation API explainer
* Domenic Denicola's Navigation API live demo


CSSMediaRule: media property - Web APIs

CSSMediaRule: media property
============================

 The read-only **`media`** property of the
 `CSSMediaRule` interface `MediaList` represents the intended
 destination medium for style information.

Value
-----

a `MediaList`

Examples
--------

 The CSS includes a media query with one style rule. This will be the first
 `CSSRule` returned by `document.styleSheets[0].cssRules`.
 Calling `myRules[0].media` therefore returns a `MediaList`
 object representing the media query.

```css
@media (min-width: 500px) {
  body {
    color: blue;
  }
}
```

```js
let myRules = document.styleSheets[0].cssRules;
console.log(myRules[0].media); // a MediaList
```

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

| Specification |
| --- |
| CSS Conditional Rules Module Level 3 # dom-cssmediarule-media |

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

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


SVGAElement: target property - Web APIs

SVGAElement: target property
============================

The **`SVGAElement.target`** read-only property of `SVGAElement` returns an `SVGAnimatedString` object that specifies the portion of a target window, frame, pane into which a document is to be opened when a link is activated.

This property is used when there are multiple possible targets for the ending resource, like when the parent document is a multi-frame HTML or XHTML document.

Value
-----

An `SVGAnimatedString` indicating the ending resource target that opens the document when the link is activated.

Sample values can be found here

Examples
--------

The code is taken from the "SVGAElement example code"

```js
// ...
const linkRef = document.querySelector("a");
linkRef.target = "\_blank";
// ...
```

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

| Specification |
| --- |
| Scalable Vector Graphics (SVG) 2 # \_\_svg\_\_SVGAElement\_\_target |

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

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

See also
--------

* `target`