Using the Web Audio API - Web APIs

Using the Web Audio API
=======================

Let's take a look at getting started with the Web Audio API. We'll briefly look at some concepts, then study a simple boombox example that allows us to load an audio track, play and pause it, and change its volume and stereo panning.

The Web Audio API does not replace the `<audio>` media element, but rather complements it, just like `<canvas>` coexists alongside the `<img>` element. Your use case will determine what tools you use to implement audio. If you want to control playback of an audio track, the `<audio>` media element provides a better, quicker solution than the Web Audio API. If you want to carry out more complex audio processing, as well as playback, the Web Audio API provides much more power and control.

A powerful feature of the Web Audio API is that it does not have a strict "sound call limitation". For example, there is no ceiling of 32 or 64 sound calls at one time. Some processors may be capable of playing more than 1,000 simultaneous sounds without stuttering.

Example code
------------

Our boombox looks like this:

![A boombox with play, pan, and volume controls](/en-US/docs/Web/API/Web_Audio_API/Using_Web_Audio_API/boombox.png)

Note the retro cassette deck with a play button, and vol and pan sliders to allow you to alter the volume and stereo panning. We could make this a lot more complex, but this is ideal for simple learning at this stage.

Check out the final demo here on Codepen, or see the source code on GitHub.

Browser support
---------------

Modern browsers have good support for most features of the Web Audio API. There are a lot of features of the API, so for more exact information, you'll have to check the browser compatibility tables at the bottom of each reference page.

Audio graphs
------------

Everything within the Web Audio API is based around the concept of an audio graph, which is made up of nodes.

The Web Audio API handles audio operations inside an **audio context**, and has been designed to allow **modular routing**. Basic audio operations are performed with **audio nodes**, which are linked together to form an **audio routing graph**. You have input nodes, which are the source of the sounds you are manipulating, modification nodes that change those sounds as desired, and output nodes (destinations), which allow you to save or hear those sounds.

Several audio sources with different channel layouts are supported, even within a single context. Because of this modular design, you can create complex audio functions with dynamic effects.

Audio context
-------------

To be able to do anything with the Web Audio API, we need to create an instance of the audio context. This then gives us access to all the features and functionality of the API.

```js
// for legacy browsers
const AudioContext = window.AudioContext || window.webkitAudioContext;

const audioContext = new AudioContext();
```

So what's going on when we do this? A `BaseAudioContext` is created for us automatically and extended to an online audio context. We'll want this because we're looking to play live sound.

**Note:** If you just want to process audio data, for instance, buffer and stream it but not play it, you might want to look into creating an `OfflineAudioContext`.

Loading sound
-------------

Now, the audio context we've created needs some sound to play through it. There are a few ways to do this with the API. Let's begin with a simple method — as we have a boombox, we most likely want to play a full song track. Also, for accessibility, it's nice to expose that track in the DOM. We'll expose the song on the page using an `<audio>` element.

```html
<audio src="myCoolTrack.mp3"></audio>
```

**Note:** If the sound file you're loading is held on a different domain you will need to use the `crossorigin` attribute; see Cross Origin Resource Sharing (CORS) for more information.

To use all the nice things we get with the Web Audio API, we need to grab the source from this element and *pipe* it into the context we have created. Lucky for us there's a method that allows us to do just that — `AudioContext.createMediaElementSource`:

```js
// get the audio element
const audioElement = document.querySelector("audio");

// pass it into the audio context
const track = audioContext.createMediaElementSource(audioElement);
```

**Note:** The `<audio>` element above is represented in the DOM by an object of type `HTMLMediaElement`, which comes with its own set of functionality. All of this has stayed intact; we are merely allowing the sound to be available to the Web Audio API.

Controlling sound
-----------------

When playing sound on the web, it's important to allow the user to control it. Depending on the use case, there's a myriad of options, but we'll provide functionality to play/pause the sound, alter the track's volume, and pan it from left to right.

Controlling sound programmatically from JavaScript code is covered by browsers' autoplay support policies, as such is likely to be blocked without permission being granted by the user (or an allowlist). Autoplay policies typically require either explicit permission or a user engagement with the page before scripts can trigger audio to play.

These special requirements are in place essentially because unexpected sounds can be annoying and intrusive, and can cause accessibility problems. You can learn more about this in our article Autoplay guide for media and Web Audio APIs.

Since our scripts are playing audio in response to a user input event (a click on a play button, for instance), we're in good shape and should have no problems from autoplay blocking. So, let's start by taking a look at our play and pause functionality. We have a play button that changes to a pause button when the track is playing:

```html
<button data-playing="false" role="switch" aria-checked="false">
  <span>Play/Pause</span>
</button>
```

Before we can play our track we need to connect our audio graph from the audio source/input node to the destination.

We've already created an input node by passing our audio element into the API. For the most part, you don't need to create an output node, you can just connect your other nodes to `BaseAudioContext.destination`, which handles the situation for you:

```js
track.connect(audioContext.destination);
```

A good way to visualize these nodes is by drawing an audio graph so you can visualize it. This is what our current audio graph looks like:

![an audio graph with an audio element source connected to the default destination](/en-US/docs/Web/API/Web_Audio_API/Using_Web_Audio_API/graph1.jpg)

Now we can add the play and pause functionality.

```js
// Select our play button
const playButton = document.querySelector("button");

playButton.addEventListener(
  "click",
  () => {
    // Check if context is in suspended state (autoplay policy)
    if (audioContext.state === "suspended") {
      audioContext.resume();
    }

    // Play or pause track depending on state
    if (playButton.dataset.playing === "false") {
      audioElement.play();
      playButton.dataset.playing = "true";
    } else if (playButton.dataset.playing === "true") {
      audioElement.pause();
      playButton.dataset.playing = "false";
    }
  },
  false,
);
```

We also need to take into account what to do when the track finishes playing. Our `HTMLMediaElement` fires an `ended` event once it's finished playing, so we can listen for that and run code accordingly:

```js
audioElement.addEventListener(
  "ended",
  () => {
    playButton.dataset.playing = "false";
  },
  false,
);
```

Modifying sound
---------------

Let's delve into some basic modification nodes, to change the sound that we have. This is where the Web Audio API really starts to come in handy. First of all, let's change the volume. This can be done using a `GainNode`, which represents how big our sound wave is.

There are two ways you can create nodes with the Web Audio API. You can use the factory method on the context itself (e.g. `audioContext.createGain()`) or via a constructor of the node (e.g. `new GainNode()`). We'll use the factory method in our code:

```js
const gainNode = audioContext.createGain();
```

Now we have to update our audio graph from before, so the input is connected to the gain, then the gain node is connected to the destination:

```js
track.connect(gainNode).connect(audioContext.destination);
```

This will make our audio graph look like this:

![an audio graph with an audio element source, connected to a gain node that modifies the audio source, and then going to the default destination](/en-US/docs/Web/API/Web_Audio_API/Using_Web_Audio_API/graph2.jpg)

The default value for gain is 1; this keeps the current volume the same. Gain can be set to a minimum of about -3.4028235E38 and a max of about 3.4028235E38 (float number range in JavaScript). Here we'll allow the boombox to move the gain up to 2 (double the original volume) and down to 0 (this will effectively mute our sound).

Let's give the user control to do this — we'll use a range input:

```html
<input type="range" id="volume" min="0" max="2" value="1" step="0.01" />
```

**Note:** Range inputs are a really handy input type for updating values on audio nodes. You can specify a range's values and use them directly with the audio node's parameters.

So let's grab this input's value and update the gain value when the input node has its value changed by the user:

```js
const volumeControl = document.querySelector("#volume");

volumeControl.addEventListener(
  "input",
  () => {
    gainNode.gain.value = volumeControl.value;
  },
  false,
);
```

**Note:** The values of node objects (e.g. `GainNode.gain`) are not simple values; they are actually objects of type `AudioParam` — these called parameters. This is why we have to set `GainNode.gain`'s `value` property, rather than just setting the value on `gain` directly. This enables them to be much more flexible, allowing for passing the parameter a specific set of values to change between over a set period of time, for example.

Great, now the user can update the track's volume! The gain node is the perfect node to use if you want to add mute functionality.

Adding stereo panning to our app
--------------------------------

Let's add another modification node to practice what we've just learnt.

There's a `StereoPannerNode` node, which changes the balance of the sound between the left and right speakers, if the user has stereo capabilities.

**Note:** The `StereoPannerNode` is for simple cases in which you just want stereo panning from left to right.
 There is also a `PannerNode`, which allows for a great deal of control over 3D space, or sound *spatialization*, for creating more complex effects.
 This is used in games and 3D apps to create birds flying overhead, or sound coming from behind the user for instance.

To visualize it, we will be making our audio graph look like this:

![An image showing the audio graph showing an input node, two modification nodes (a gain node and a stereo panner node) and a destination node.](/en-US/docs/Web/API/Web_Audio_API/Using_Web_Audio_API/graphpan.jpg)

Let's use the constructor method of creating a node this time. When we do it this way, we have to pass in the context and any options that the particular node may take:

```js
const pannerOptions = { pan: 0 };
const panner = new StereoPannerNode(audioContext, pannerOptions);
```

**Note:** The constructor method of creating nodes is not supported by all browsers at this time. The older factory methods are supported more widely.

Here our values range from -1 (far left) and 1 (far right). Again let's use a range type input to vary this parameter:

```html
<input type="range" id="panner" min="-1" max="1" value="0" step="0.01" />
```

We use the values from that input to adjust our panner values in the same way as we did before:

```js
const pannerControl = document.querySelector("#panner");

pannerControl.addEventListener(
  "input",
  () => {
    panner.pan.value = pannerControl.value;
  },
  false,
);
```

Let's adjust our audio graph again, to connect all the nodes together:

```js
track.connect(gainNode).connect(panner).connect(audioContext.destination);
```

The only thing left to do is give the app a try: Check out the final demo here on Codepen.

Summary
-------

Great! We have a boombox that plays our 'tape', and we can adjust the volume and stereo panning, giving us a fairly basic working audio graph.

This makes up quite a few basics that you would need to start to add audio to your website or web app. There's a lot more functionality to the Web Audio API, but once you've grasped the concept of nodes and putting your audio graph together, we can move on to looking at more complex functionality.

More examples
-------------

There are other examples available to learn more about the Web Audio API.

The Voice-change-O-matic is a fun voice manipulator and sound visualization web app that allows you to choose different effects and visualizations. The application is fairly rudimentary, but it demonstrates the simultaneous use of multiple Web Audio API features. (run the Voice-change-O-matic live).

![A UI with a sound wave being shown, and options for choosing voice effects and visualizations.](/en-US/docs/Web/API/Web_Audio_API/Using_Web_Audio_API/voice-change-o-matic.png)

Another application developed specifically to demonstrate the Web Audio API is the Violent Theremin, a simple web application that allows you to change pitch and volume by moving your mouse pointer. It also provides a psychedelic lightshow (see Violent Theremin source code).

![A page full of rainbow colors, with two buttons labeled Clear screen and mute.](/en-US/docs/Web/API/Web_Audio_API/Using_Web_Audio_API/violent-theremin.png)

Also see our webaudio-examples repo for more examples.


Controlling multiple parameters with ConstantSourceNode - Web APIs

Controlling multiple parameters with ConstantSourceNode
=======================================================

This article demonstrates how to use a `ConstantSourceNode` to link multiple parameters together so they share the same value, which can be changed by setting the value of the `ConstantSourceNode.offset` parameter.

You may sometimes want multiple audio parameters to be linked so they share the same value while being changed somehow. For example, perhaps you have a set of oscillators, two of which need to share the same configurable volume, or you have a filter applied to specific inputs but not all of them. You could use a loop and change the value of each affected `AudioParam` one at a time. Still, there are two drawbacks to doing it that way: first, that's extra code that, as you're about to see, you don't have to write; and second, that loop uses valuable CPU time on your thread (likely the main thread), and there's a way to offload all that work to the audio rendering thread, which is optimized for this kind of work and may run at a more appropriate priority level than your code.

The solution is simple, and it involves using an audio node type that, at first glance, doesn't look all that useful: `ConstantSourceNode`.

The technique
-------------

Using a `ConstantSourceNode` is an effortless way to do something that sounds like it might be hard. You need to create a `ConstantSourceNode` and connect it to all of the `AudioParam`s whose values should be linked to always match each other. Since `ConstantSourceNode`'s `offset` value is sent straight through to all of its outputs, it acts as a splitter for that value, sending it to each connected parameter.

The diagram below shows how this works; an input value, `N`, is set as the value of the `ConstantSourceNode.offset` property. The `ConstantSourceNode` can have as many outputs as necessary; in this case, we've connected it to three nodes: two `GainNode`s and a `StereoPannerNode`. So `N` becomes the value of the specified parameter (`gain` for the `GainNode`s and pan for the `StereoPannerNode`.

![Diagram in SVG showing how ConstantSourceNode can be used to split an input parameter to share it with multiple nodes.](/en-US/docs/Web/API/Web_Audio_API/Controlling_multiple_parameters_with_ConstantSourceNode/customsourcenode-as-splitter.svg)

As a result, every time you change `N` (the value of the input `AudioParam`, the values of the two `GainNode.gain` properties and the value of the `StereoPannerNode` 's `pan` properties are all set to `N` as well.

Example
-------

Let's take a look at this technique in action. In this simple example, we create three `OscillatorNode` objects. Two of them have adjustable gain, controlled using a shared input control. The other oscillator has a fixed volume.

### HTML

The HTML content for this example is primarily a checkbox, shaped as an actual button, to toggle the oscillator tones on and off and an `<input>` element of type `range` to control the volume of two of the three oscillators.

```html
<div class="controls">
    <input type="checkbox" id="playButton">
    <label for="playButton">Activate: </label>
    <label for="volumeControl">Volume: </label>
    <input type="range" min="0.0" max="1.0" step="0.01"
 value="0.8" name="volume" id="volumeControl">
  </div>
</div>

<p>Toggle the checkbox above to start and stop the tones, and use the volume control to
change the volume of the notes E and G in the chord.</p>
```

```
.controls {
  width: 400px;
  position: relative;
  vertical-align: middle;
  height: 44px;
}

#playButton:checked + label::after {
  content: "⏸";
}

#playButton:not(checked) + label::after {
  content: "▶";
}

#playButton + label::after {
  cursor: pointer;
}

#playButton {
  vertical-align: middle;
  display: none;
}

#volumeControl {
  vertical-align: bottom;
}

label {
  vertical-align: middle;
}
```

### JavaScript

Now let's look at the JavaScript code, a piece at a time.

#### Setting up

Let's start by looking at the global variable initialization.

```js
// Useful UI elements
const playButton = document.querySelector("#playButton");
const volumeControl = document.querySelector("#volumeControl");

// The audio context and the node will be initialized after the first request
let context = null;
let oscNode1 = null;
let oscNode2 = null;
let oscNode3 = null;
let constantNode = null;
let gainNode1 = null;
let gainNode2 = null;
let gainNode3 = null;
```

These variables are:

`context`

The `AudioContext` in which all the audio nodes live; it will be initialized during after a user-action.

`playButton` and `volumeControl`

References to the play button and volume control elements.

`oscNode1`, `oscNode2`, and `oscNode3`

The three `OscillatorNode`s used to generate the chord.

`gainNode1`, `gainNode2`, and `gainNode3`

The three `GainNode` instances which provide the volume levels for each of the three oscillators. `gainNode2` and `gainNode3` will be linked together to have the same, adjustable, value using the `ConstantSourceNode`.

`constantNode`

The `ConstantSourceNode` used to control the values of `gainNode2` and `gainNode3` together.

Now let's look at the `setup()` function, called when the user toggles the play button for the first time; it handles all the initialization tasks to set up the audio graph.

```js
function setup() {
  context = new AudioContext();

  gainNode1 = new GainNode(context, {
    gain: 0.5,
  });
  gainNode2 = new GainNode(context, {
    gain: gainNode1.gain.value,
  });
  gainNode3 = new GainNode(context, {
    gain: gainNode1.gain.value,
  });

  volumeControl.value = gainNode1.gain.value;

  constantNode = new ConstantSourceNode(context, {
    offset: volumeControl.value,
  });
  constantNode.connect(gainNode2.gain);
  constantNode.connect(gainNode3.gain);
  constantNode.start();

  gainNode1.connect(context.destination);
  gainNode2.connect(context.destination);
  gainNode3.connect(context.destination);

  // All is set up. We can hook the volume control.
  volumeControl.addEventListener("input", changeVolume, false);
}
```

First, we get access to the window's `AudioContext`, stashing the reference in `context`. Then we get references to the control widgets, setting `playButton` to reference the play button and `volumeControl` to reference the slider control that the user will use to adjust the gain on the linked pair of oscillators.

Next, the `GainNode` `gainNode1` is created to handle the volume for the non-linked oscillator (`oscNode1`). We set that gain to 0.5. We also create `gainNode2` and `gainNode3`, set their values to match `gainNode1`, then set the value of the volume slider to the same value, so it stays synchronized with the gain level it controls.

Once all the gain nodes are created, we create the `ConstantSourceNode`, `constantNode`. We connect its output to the `gain` `AudioParam` on both `gainNode2` and `gainNode3`, and we start the constant node running by calling its `start()` method; now it's sending the value 0.5 to the two gain nodes' values, and any change to `constantNode.offset` will automatically set the gain of both `gainNode2` and `gainNode3` (affecting their audio inputs as expected).

Finally, we connect all the gain nodes to the `AudioContext`'s `destination`, so that any sound delivered to the gain nodes will reach the output, whether that output be speakers, headphones, a recording stream, or any other destination type.

Then we assign a handler for the volume slider's `input` event (see Controlling the linked oscillators to see the very short `changeVolume()` method).

Right after declaring the `setup()` function, we add a handler to the play checkbox's `change` event (see Toggling the oscillators on and off for more on the `togglePlay()` method), and the stage is set. Let's see how the action plays out.

```js
playButton.addEventListener("change", togglePlay, false);
```

#### Toggling the oscillators on and off

Because `OscillatorNode` doesn't support the notion of being in a paused state, we have to simulate it by terminating the oscillators and starting them again when the user clicks on the play checkbox again to toggle them back on. Let's look at the code.

```js
function togglePlay(event) {
  if (!playButton.checked) {
    stopOscillators();
  } else {
    // If it is the first start, initialize the audio graph
    if (!context) {
      setup();
    }
    startOscillators();
  }
}
```

If the `playButton` widget is checked, we're already playing the oscillators, and we call `stopOscillators()` to shut down the oscillators. See Stopping the oscillators below for that code.

If the `playButton` widget is checked, indicating that we're currently paused, we call `startOscillators()` to start the oscillators playing their tones. Below, we describe that code under Starting the oscillators.

#### Controlling the linked oscillators

The `changeVolume()` function, the event handler for the slider control for the gain on the linked oscillator pair, looks like this:

```js
function changeVolume(event) {
  constantNode.offset.value = volumeControl.value;
}
```

That simple function controls the gain on both nodes. All we have to do is set the value of the `ConstantSourceNode`'s `offset` parameter. That value becomes the node's constant output value, fed to all its outputs, `gainNode2` and `gainNode3`.

While this is an elementary example, imagine having a 32 oscillator synthesizer with multiple linked parameters in play across many patched nodes. Shortening the number of operations to adjust them all will prove invaluable for both code size and performance.

#### Starting the oscillators

When the user clicks the play/pause toggle button while the oscillators aren't playing, the `startOscillators()` function gets called.

```js
function startOscillators() {
  oscNode1 = new OscillatorNode(context, {
    type: "sine",
    frequency: 261.625565300598634, // middle C$
  });
  oscNode1.connect(gainNode1);

  oscNode2 = new OscillatorNode(context, {
    type: "sine",
    frequency: 329.627556912869929, // E
  });
  oscNode2.connect(gainNode2);

  oscNode3 = new OscillatorNode(context, {
    type: "sine",
    frequency: 391.995435981749294, // G
  });
  oscNode3.connect(gainNode3);

  oscNode1.start();
  oscNode2.start();
  oscNode3.start();
}
```

Each of the three oscillators is set up the same way, creating the `OscillatorNode` by calling the `OscillatorNode()` constructor with two options:

1. Set the oscillator's `type` to `"sine"` to use a sine wave as the audio waveform.
2. Set the oscillator's `frequency` to the desired value; in this case, `oscNode1` is set to a middle C, while `oscNode2` and `oscNode3` round out the chord by playing the E and G notes.

Then, we connect the new oscillator to the corresponding gain node.

Once all three oscillators have been created, they're started by calling each one's `ConstantSourceNode.start()` method in turn.

#### Stopping the oscillators

Stopping the oscillators when the user toggles the play state to pause the tones is as simple as stopping each node.

```js
function stopOscillators() {
  oscNode1.stop();
  oscNode2.stop();
  oscNode3.stop();
}
```

Each node is stopped by calling its `ConstantSourceNode.stop()` method.

### Result

See also
--------

* Web Audio API
* Using the Web Audio API
* Simple synth keyboard (example)
* `OscillatorNode`
* `ConstantSourceNode`


RelativeOrientationSensor: RelativeOrientationSensor() constructor - Web APIs

RelativeOrientationSensor: RelativeOrientationSensor() constructor
==================================================================

 The **`RelativeOrientationSensor()`**
 constructor creates a new `RelativeOrientationSensor` object which
 describes the device's physical orientation.

Syntax
------

```js
new RelativeOrientationSensor()
new RelativeOrientationSensor(options)
```

### Parameters

`options` Optional

Options are as follows:

`frequency`

 The desired number of times per second a sample
 should be taken, meaning the number of times per second that the
 `reading` event will be called. A whole number or decimal
 may be used, the latter for frequencies less than a second. The actual
 reading frequency depends device hardware and consequently may be less
 than requested.

`referenceFrame`

 Either `'device'` or
 `'screen'`. The default is `'device'`.

### Exceptions

`SecurityError` `DOMException`

Use of this feature was blocked by a Permissions Policy.

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

| Specification |
| --- |
| Orientation Sensor # dom-relativeorientationsensor-relativeorientationsensor |

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

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

See also
--------

* `reading` event


PerformanceMark: detail property - Web APIs

PerformanceMark: detail property
================================

The read-only **`detail`** property returns arbitrary metadata that was included in the mark upon construction (either when using `performance.mark()` or the `PerformanceMark()` constructor).

Value
-----

Returns the value it is set to (from `markOptions` of `performance.mark()` or the `PerformanceMark()` constructor).

Examples
--------

The following example demonstrates the `detail` property.

```js
performance.mark("dog", { detail: "labrador" });

const dogEntries = performance.getEntriesByName("dog");

dogEntries[0].detail; // labrador
```

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

| Specification |
| --- |
| User Timing # dom-performancemark-detail |

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

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


PerformanceMark: PerformanceMark() constructor - Web APIs

PerformanceMark: PerformanceMark() constructor
==============================================

The **`PerformanceMark()`** constructor creates a `timestamp` with the given name.

Unlike `performance.mark()`, performance marks created by the constructor aren't added to the browser's performance timeline. This means that calls to the `Performance` interface's `getEntries*()` methods (`getEntries()`, `getEntriesByName()` or `getEntriesByType()`) won't show entries for these marks.

Syntax
------

```js
new PerformanceMark(name)
new PerformanceMark(name, markOptions)
```

### Parameters

`name`

A string representing the name of the mark.

`markOptions` Optional

An object for specifying a timestamp and additional metadata for the mark.

`detail` Optional

Arbitrary metadata to include in the mark. Defaults to `null`.

`startTime` Optional

`DOMHighResTimeStamp` to use as the mark time. Defaults to `performance.now()`.

### Return value

A `PerformanceMark` object.

### Exceptions

* `SyntaxError`: Thrown if the `name` given to this method already exists in the `PerformanceTiming` interface.
* `TypeError`: Thrown if `startTime` is negative.
Examples
--------

The following example shows how `PerformanceMark` entries are constructed and then aren't part of the browser's performance timeline.

```js
new PerformanceMark("squirrel");
new PerformanceMark("monkey");
new PerformanceMark("dog");

const allEntries = performance.getEntriesByType("mark");
console.log(allEntries.length);
// 0
```

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

| Specification |
| --- |
| User Timing # dom-performancemark-constructor |

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

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

See also
--------

* `performance.mark()`


AnimationPlaybackEvent: AnimationPlaybackEvent() constructor - Web APIs

AnimationPlaybackEvent: AnimationPlaybackEvent() constructor
============================================================

Baseline 2022
-------------

Newly availableSince September 2022, this feature works across the latest devices and browser versions. This feature might not work in older devices or browsers.

* Learn more
* See full compatibility
* Report feedback
The **`AnimationPlaybackEvent()`** constructor of the Web Animations API returns a new `AnimationPlaybackEvent` object instance.

Syntax
------

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

### Parameters

`type`

 A string with the name of the event.
 It is case-sensitive and browsers set it to `cancel`, `finish`, or `remove`.

`options` Optional

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

`detail` Optional

An event-dependent value associated with the event. Defaults to `null`.

### Return value

A new `AnimationPlaybackEvent` object.

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

| Specification |
| --- |
| Web Animations # dom-animationplaybackevent-animationplaybackevent |

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

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

See also
--------

* Web Animations API
* `AnimationPlayBackEvent`
* `Animation.playState`
* `CustomEvent()`
* `Event()`


AnimationPlaybackEvent: currentTime property - Web APIs

AnimationPlaybackEvent: currentTime property
============================================

Baseline 2022
-------------

Newly availableSince September 2022, this feature works across the latest devices and browser versions. This feature might not work in older devices or browsers.

* Learn more
* See full compatibility
* Report feedback
The **`currentTime`** read-only property of the `AnimationPlaybackEvent` interface represents the current time of the animation that generated the event at the moment the event is queued. This will be unresolved if the animation was `idle` at the time the event was generated.

Value
-----

A number representing the current time in milliseconds, or `null`.

Reduced time precision
----------------------

 To offer protection against timing attacks and fingerprinting, the precision of `playbackEvent.currentTime` might get rounded depending on browser settings.
 In Firefox, the `privacy.reduceTimerPrecision` preference is enabled by default and defaults to 20 µs in Firefox 59; in 60, it will be 2 ms.

```js
// reduced time precision (2ms) in Firefox 60
playbackEvent.currentTime;
// 23.404
// 24.192
// 25.514
// …

// reduced time precision with `privacy.resistFingerprinting` enabled
playbackEvent.currentTime;
// 49.8
// 50.6
// 51.7
// …
```

In Firefox, you can also enabled `privacy.resistFingerprinting`, the precision will be 100ms or the value of `privacy.resistFingerprinting.reduceTimerPrecision.microseconds`, whichever is larger.

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

| Specification |
| --- |
| Web Animations # dom-animationplaybackevent-currenttime |

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

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

See also
--------

* Web Animations API
* `AnimationPlayBackEvent`


AnimationPlaybackEvent: timelineTime property - Web APIs

AnimationPlaybackEvent: timelineTime property
=============================================

Baseline 2022
-------------

Newly availableSince September 2022, this feature works across the latest devices and browser versions. This feature might not work in older devices or browsers.

* Learn more
* See full compatibility
* Report feedback
The **`timelineTime`** read-only property of the `AnimationPlaybackEvent` interface represents the time value of the animation's `timeline` at the moment the event is queued. This will be unresolved if the animation was not associated with a timeline at the time the event was generated or if the associated timeline was inactive.

Value
-----

A number representing the current time in milliseconds, or `null`.

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

| Specification |
| --- |
| Web Animations # dom-animationplaybackevent-timelinetime |

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

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

See also
--------

* Web Animations API
* `AnimationPlayBackEvent`
* `AnimationTimeline`


PerformanceLongTaskTiming: toJSON() method - Web APIs

PerformanceLongTaskTiming: toJSON() method
==========================================

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

The **`toJSON()`** method of the `PerformanceLongTaskTiming` interface is a serializer; it returns a JSON representation of the `PerformanceLongTaskTiming` object.

Syntax
------

```js
toJSON()
```

### Parameters

None.

### Return value

A `JSON` object that is the serialization of the `PerformanceLongTaskTiming` object.

Examples
--------

### Using the toJSON method

In this example, calling `entry.toJSON()` returns a JSON representation of the `PerformanceLongTaskTiming` object.

```js
const observer = new PerformanceObserver((list) => {
  list.getEntries().forEach((entry) => {
    console.log(entry.toJSON());
  });
});

observer.observe({ type: "longtask", buffered: true });
```

This would log a JSON object like so:

```json
{
  "name": "self",
  "entryType": "longtask",
  "startTime": 183,
  "duration": 60,
  "attribution": [
    {
      "name": "unknown",
      "entryType": "taskattribution",
      "startTime": 0,
      "duration": 0,
      "containerType": "window",
      "containerSrc": "",
      "containerId": "",
      "containerName": ""
    }
  ]
}
```

To get a JSON string, you can use `JSON.stringify(entry)` directly; it will call `toJSON()` automatically.

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

| Specification |
| --- |
| Long Tasks API # dom-performancelongtasktiming-tojson |

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

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

See also
--------

* `JSON`


PerformanceLongTaskTiming: attribution property - Web APIs

PerformanceLongTaskTiming: attribution property
===============================================

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

The **`attribution`** readonly property of the `PerformanceLongTaskTiming` interface returns an array of `TaskAttributionTiming` objects.

Value
-----

An `Array` of `TaskAttributionTiming` objects.

Examples
--------

### Logging attributions for long tasks

```js
const observer = new PerformanceObserver((list) => {
  list.getEntries().forEach((entry) => {
    entry.attribution.forEach((attributionEntry) => {
      console.log(attributionEntry);
    });
  });
});

observer.observe({ type: "longtask", buffered: true });
```

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

| Specification |
| --- |
| Long Tasks API # dom-performancelongtasktiming-attribution |

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

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

See also
--------

* `TaskAttributionTiming`


ConvolverNode: ConvolverNode() constructor - Web APIs

ConvolverNode: ConvolverNode() constructor
==========================================

 The **`ConvolverNode()`** constructor of the Web Audio API creates a new
 `ConvolverNode` object instance.

Syntax
------

```js
new ConvolverNode(context, options)
```

### Parameters

`context`

A reference to an `AudioContext`.

`options` Optional

Options are as follows:

`buffer`

 A mono, stereo, or
 4-channel `AudioBuffer` containing the
 (possibly multichannel) impulse response used by the `ConvolverNode`
 to create the reverb effect.

`disableNormalization`

 A boolean value controlling
 whether the impulse response from the buffer will be scaled by an equal-power
 normalization, or not. The default is '`false`'.

`channelCount`

 Represents an integer used to determine how many channels are used
 when up-mixing and down-mixing connections to any inputs to the node.
 (See `AudioNode.channelCount` for more information.)
 Its usage and precise definition depend on the value of `channelCountMode`.

`channelCountMode`

 Represents an enumerated value describing the way channels must be matched between the node's inputs and outputs.
 (See `AudioNode.channelCountMode` for more information including default values.)

`channelInterpretation`

 Represents an enumerated value describing the meaning of the channels.
 This interpretation will define how audio up-mixing and down-mixing will happen.
 The possible values are `"speakers"` or `"discrete"`.
 (See `AudioNode.channelCountMode` for more information including default values.)

### Return value

A new `ConvolverNode` object instance.

### Exceptions

`NotSupportedError` `DOMException`

 Thrown if the referenced `AudioBuffer` does not have the
 correct number of channels, or it has a different sample rate to the
 associated `AudioContext`.

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

| Specification |
| --- |
| Web Audio API # dom-convolvernode-constructor-convolvernode |

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

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


ConvolverNode: normalize property - Web APIs

ConvolverNode: normalize property
=================================

 The `normalize` property of the `ConvolverNode` interface
 is a boolean that controls whether the impulse response from the buffer will be
 scaled by an equal-power normalization when the `buffer` attribute is set,
 or not.

 Its default value is `true` in order to achieve a more uniform output
 level from the convolver, when loaded with diverse impulse responses. If normalize is
 set to `false`, then the convolution will be rendered with no
 pre-processing/scaling of the impulse response. Changes to this value do not take
 effect until the next time the `buffer` attribute is set.

Value
-----

A boolean.

Examples
--------

### Switching normalization off

The following example creates a convolver node and assigns it an `AudioBuffer`. Before assigning the audio buffer, it sets `normalize` to `false`.

```js
const audioCtx = new AudioContext();
// ...

const convolver = audioCtx.createConvolver();
// ...

// Grab audio track via fetch() for convolver node
try {
  const response = await fetch(
    "https://mdn.github.io/voice-change-o-matic/audio/concert-crowd.ogg",
  );
  const arrayBuffer = await response.arrayBuffer();
  const decodedAudio = await audioCtx.decodeAudioData(arrayBuffer);
  convolver.normalize = false; // must be set before the buffer, to take effect
  convolver.buffer = decodedAudio;
} catch (error) {
  console.error(
    `Unable to fetch the audio file: ${name} Error: ${err.message}`,
  );
}
```

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

| Specification |
| --- |
| Web Audio API # dom-convolvernode-normalize |

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

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

See also
--------

* Using the Web Audio API


ConvolverNode: buffer property - Web APIs

ConvolverNode: buffer property
==============================

The **`buffer`** property of the `ConvolverNode` interface represents a mono, stereo, or 4-channel `AudioBuffer` containing the (possibly multichannel) impulse response used by the `ConvolverNode` to create the reverb effect.

This is normally a simple recording of as-close-to-an-impulse as can be found in the space you want to model. For example, if you want to model the reverb in your bathroom, you might set up a microphone near the door to record the sound of a balloon pop or synthesized impulse from the sink. That audio recording could then be used as the buffer.

This audio buffer must have the same sample-rate as the `AudioContext` or an exception will be thrown. At the time when this attribute is set, the buffer and the state of the attribute will be used to configure the `ConvolverNode` with this impulse response having the given normalization. The initial value of this attribute is `null`.

Value
-----

An `AudioBuffer`.

Examples
--------

### Assigning an audio buffer

The following example creates a convolver node and assigns it an `AudioBuffer`.

For more complete applied examples/information, check out our Voice-change-O-matic demo (see app.js for the code that is excerpted below).

```js
const audioCtx = new AudioContext();
// ...

const convolver = audioCtx.createConvolver();
// ...

// Grab audio track via fetch() for convolver node
try {
  const response = await fetch(
    "https://mdn.github.io/voice-change-o-matic/audio/concert-crowd.ogg",
  );
  const arrayBuffer = await response.arrayBuffer();
  const decodedAudio = await audioCtx.decodeAudioData(arrayBuffer);
  convolver.buffer = decodedAudio;
} catch (error) {
  console.error(
    `Unable to fetch the audio file: ${name} Error: ${err.message}`,
  );
}
```

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

| Specification |
| --- |
| Web Audio API # dom-convolvernode-buffer |

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

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

See also
--------

* Using the Web Audio API


XRPose: emulatedPosition property - Web APIs

XRPose: emulatedPosition property
=================================

 The `emulatedPosition` read-only attribute of the
 `XRPose` interface is a Boolean value indicating whether or not both the
 `position` component of the pose's
 `transform` is directly taken from the XR device, or
 it's simulated or computed based on other sources.

Value
-----

 A Boolean which is `true` if the pose's position is computed based on
 estimates or is derived from sources other than direct sensor data. If the position is
 precisely based on direct sensor inputs, the value is `false`.

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

 There are two basic categories of XR tracking systems. A basic XR headset provides
 three degrees of freedom (3DoF), tracking the pitch, yaw, and roll of the user's head.
 No information is available about movement forward, backward, or to the sides. Any such
 data is taken from other sources, such as keyboard or mouse inputs or game controllers.
 As such, the position is considered to be emulated, so the `emulatedPosition`
 property is `true`.

 Contrariwise, XR devices which can also track movement forward and backward as well as
 laterally—six degree of freedom (6DoF) devices—don't require any information from other
 sources to determine the user's position, so the value of `emulatedPosition`
 is `false`.

 The same notion applies not just to the user's head, but to any object. A hand
 controller that can directly report its position would have a value of
 `false` for this property as well. If its position is computed as an offset
 from another object (such as by basing it off the model representing the user's body),
 then this value is `true`.

 This information is important because devices whose position is emulated are prone to
 their offset drifting relative to the real world space over time. This is because
 emulating a position based on accelerometer inputs and models tends to introduce minor
 errors which accumulate over time.

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

| Specification |
| --- |
| WebXR Device API # dom-xrpose-emulatedposition |

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

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


XRPose: linearVelocity property - Web APIs

XRPose: linearVelocity property
===============================

 The `linearVelocity` read-only property of the
 `XRPose` interface is a `DOMPointReadOnly` describing
 the linear velocity in meters per second relative to the base
 `XRSpace`.

Value
-----

 A `DOMPointReadOnly` describing the linear velocity in meters
 per second relative to the base `XRSpace`. Returns `null`
 if the user agent can't populate this value.

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

| Specification |
| --- |
| WebXR Device API # dom-xrpose-linearvelocity |

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

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

See also
--------

* `XRPose.linearVelocity`


XRPose: angularVelocity property - Web APIs

XRPose: angularVelocity property
================================

 The `angularVelocity` read-only property of the
 `XRPose` interface is a `DOMPointReadOnly` describing
 the angular velocity in radians per second relative to the base
 `XRSpace`.

Value
-----

 A `DOMPointReadOnly` describing the angular velocity in radians
 per second relative to the base `XRSpace`. Returns `null`
 if the user agent can't populate this value.

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

| Specification |
| --- |
| WebXR Device API # dom-xrpose-angularvelocity |

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

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

See also
--------

* `XRPose.linearVelocity`


XRPose: transform property - Web APIs

XRPose: transform property
==========================

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

 The `transform` read-only attribute of the
 `XRPose` interface is a `XRRigidTransform` object providing
 the position and orientation of the pose relative to the base `XRSpace`
 as specified when the pose was obtained by calling
 `XRFrame.getPose()`.

Value
-----

 An `XRRigidTransform` which provides the position and orientation of the
 `XRPose` relative to the `XRFrame` to which this
 `XRPose` is aligned. This is the same pose that's returned by the frame's
 `getPose()` method.

Examples
--------

 This handler for the `XRSession` event `select` handles events for tracked pointers. It determines the targeted object by
 passing the event frame's pose into a function called `findTargetUsingRay()`,
 then dispatches the event differently depending on the user's handedness; this is done
 by comparing the value of the `XRInputSource` property
 `handedness` to a value in the variable
 `user.handedness`. If the source is a controller in the user's primary hand,
 a function is called on the targeted object called `primaryAction()`;
 otherwise, it calls the targeted object's `offHandAction()` function.

```js
xrSession.addEventListener("select", (event) => {
  let source = event.inputSource;
  let frame = event.frame;
  let targetRayPose = frame.getPose(source.targetRaySpace, myRefSpace);
  let targetObject = findTargetUsingRay(targetRay.transform.matrix);

  if (source.targetRayMode === "tracked-pointer") {
    if (source.handedness === user.handedness) {
      targetObject.primaryAction();
    } else {
      targetObject.offHandAction();
    }
  }
});
```

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

| Specification |
| --- |
| WebXR Device API # dom-xrpose-transform |

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

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


CSSStyleRule: selectorText property - Web APIs

CSSStyleRule: selectorText property
===================================

The **`selectorText`** property of the `CSSStyleRule` interface gets and sets the selectors associated with the `CSSStyleRule`.

Value
-----

A string.

Examples
--------

The CSS includes one style rule. This will be the first `CSSRule` returned by `document.styleSheets[0].cssRules`. `myRules[0].selectorText` therefore returns a literal string of the selector, in this case `"h1"`.

```css
h1 {
  color: pink;
}
```

```js
let myRules = document.styleSheets[0].cssRules;
console.log(myRules[0].selectorText); // a string containing "h1".
```

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

| Specification |
| --- |
| CSS Object Model (CSSOM) # dom-cssstylerule-selectortext |

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

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


CSSStyleRule: style property - Web APIs

CSSStyleRule: style property
============================

The read-only **`style`** property is the `CSSStyleDeclaration` interface for the declaration block of the `CSSStyleRule`.

Value
-----

A `CSSStyleDeclaration` object, with the following properties:

computed flag

Unset.

declarations

The declared declarations in the rule, in the order they were specified, shorthand properties expanded to longhands.

parent CSS rule

The context object, which is an alias for this.

owner node

Null.

Examples
--------

 The CSS includes one style rule. This will be the first `CSSRule` returned by `document.styleSheets[0].cssRules`.
 `myRules[0].style` therefore returns a `CSSStyleDeclaration` object representing the declarations defined for `h1`.

```css
h1 {
  color: pink;
}
```

```js
let myRules = document.styleSheets[0].cssRules;
console.log(myRules[0].style); // a CSSStyleDeclaration representing the declarations on the h1.
```

**Note:** The declaration block is that part of the style rule that appears within the braces and that actually provides the style definitions (for the selector, the part that comes before the braces).

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

| Specification |
| --- |
| CSS Object Model (CSSOM) # dom-cssstylerule-style |

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

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


CSSStyleRule: styleMap property - Web APIs

CSSStyleRule: styleMap property
===============================

 The **`styleMap`** read-only property of the
 `CSSStyleRule` interface returns a `StylePropertyMap` object
 which provides access to the rule's property-value pairs.

Value
-----

A `StylePropertyMap` object.

Example
-------

 The following example shows `styleMap` being used to modify a style using
 the `StylePropertyMap.set()` method.

```js
const stylesheet = document.styleSheets[0];

Object.values(stylesheet.cssRules).forEach((block) => {
  if (block.selectorText === "button") {
    block.styleMap.set("--mainColor", "black");
  }
});
```

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

| Specification |
| --- |
| CSS Typed OM Level 1 # dom-cssstylerule-stylemap |

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

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


MathMLElement: style property - Web APIs

MathMLElement: style property
=============================

The read-only **`style`** property of the `MathMLElement` returns the *inline* style of an element in the form of a live `CSSStyleDeclaration` object that contains a list of all styles properties for that element with values assigned only for the attributes that are defined in the element's inline `style` attribute.

Shorthand properties are expanded. If you set `style="border-top: 1px solid black"`, the longhand properties (`border-top-color`, `border-top-style`, and `border-top-width`) are set instead.

This property is read-only, meaning it is not possible to assign a `CSSStyleDeclaration` object to it. Nevertheless, it is possible to set an inline style by assigning a *string* directly to the `style` property. In this case the string is forwarded to `CSSStyleDeclaration.cssText`. Using `style` in this manner will completely overwrite all inline styles on the element.

Therefore, to add specific styles to an element without altering other style values, it is generally preferable to set individual properties on the `CSSStyleDeclaration` object. For example, you can write `element.style.backgroundColor = "red"`.

A style declaration is reset by setting it to `null` or an empty string, e.g., `elt.style.color = null`.

**Note:** CSS property names are converted to JavaScript identifier with these rules:

* If the property is made of one word, it remains as it is: `height` stays as is (in lowercase).
* If the property is made of several words, separated by dashes, the dashes are removed and it is converted to camel case: `background-attachment` becomes `backgroundAttachment`.
* The property `float`, being a reserved JavaScript keyword, is converted to `cssFloat`.

The `style` property has the same priority in the CSS cascade as an inline style declaration set via the `style` attribute.

Value
-----

A live `CSSStyleDeclaration` object.

Examples
--------

### Getting style information

The following code snippet demonstrates how the `style` attribute is translated into a list of entries in `CSSStyleDeclaration` :

```html
<math xmlns="http://www.w3.org/1998/Math/MathML">
  <mrow>
    <mi>f</mi>
    <mo stretchy="false">(</mo>
    <mi class="parameter" style="color: red; border-bottom: 1px solid">x</mi>
    <mo stretchy="false">)</mo>
    <mo>=</mo>
    <mi>x</mi>
  </mrow>
</math>
<pre id="out"></pre>
```

```js
const element = document.querySelector(".parameter");
const out = document.getElementById("out");
const elementStyle = element.style;

// We loop through all styles (for…of doesn't work with CSStyleDeclaration)
for (const prop in elementStyle) {
  if (Object.hasOwn(elementStyle, prop)) {
    out.textContent += `${
 elementStyle[prop]
 } = '${elementStyle.getPropertyValue(elementStyle[prop])}'\n`;
  }
}
```

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

| Specification |
| --- |
| CSS Object Model (CSSOM) # dom-elementcssinlinestyle-style |

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

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

See also
--------

* Using dynamic styling information
* `HTMLElement.style`
* `SVGElement.style`


XRHitTestSource: cancel() method - Web APIs

XRHitTestSource: cancel() method
================================

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

The **`cancel()`** method of the `XRHitTestSource` interface unsubscribes a hit test.

Syntax
------

```js
cancel()
```

### Parameters

None.

### Return value

None (`undefined`).

Examples
--------

### Unsubscribe from hit test

The `cancel()` method unsubscribes from a hit test source. Since the `XRHitTestSource` object will no longer be usable, you can clean up and set it to `null`.

```js
hitTestSource.cancel();
hitTestSource = null;
```

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

| Specification |
| --- |
| WebXR Hit Test Module # dom-xrhittestsource-cancel |

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

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


Magnetometer: Magnetometer() constructor - Web APIs

Magnetometer: Magnetometer() constructor
========================================

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

 The **`Magnetometer()`** constructor
 creates a new `Magnetometer` object which returns information about the
 magnetic field as detected by a device's primary magnetometer sensor.

Syntax
------

```js
new Magnetometer()
new Magnetometer(options)
```

### Parameters

`options` Optional

Options are as follows:

`frequency`

 The desired number of times per second a sample should
 be taken, meaning the number of times per second that the
 `reading` event will be called. A whole number or decimal may be
 used, the latter for frequencies less than a second. The actual reading frequency
 depends on device hardware and consequently may be less than requested.

`referenceFrame`

 Either `'device'` or
 `'screen'`. The default is `'device'`.

### Exceptions

`SecurityError` `DOMException`

Use of this feature was blocked by a Permissions Policy.

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

| Specification |
| --- |
| Magnetometer # dom-magnetometer-magnetometer |

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

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

See also
--------

* `reading` event


Magnetometer: z property - Web APIs

Magnetometer: z property
========================

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

 The **`z`** read-only property of the
 `Magnetometer` interface returns a number specifying
 the magnetic field around the device's z-axis.

Value
-----

A `Number`.

Examples
--------

 The magnetometer is typically read in the `reading` event
 callback. In the example below this occurs sixty times a second.

```js
let magSensor = new Magnetometer({ frequency: 60 });

magSensor.addEventListener("reading", (e) => {
  console.log(`Magnetic field along the X-axis ${magSensor.x}`);
  console.log(`Magnetic field along the Y-axis ${magSensor.y}`);
  console.log(`Magnetic field along the Z-axis ${magSensor.z}`);
});
magSensor.start();
```

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

| Specification |
| --- |
| Magnetometer # magnetometer-z |

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

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


Magnetometer: x property - Web APIs

Magnetometer: x property
========================

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

 The **`x`** read-only property of the
 `Magnetometer` interface returns a number specifying
 the magnetic field around the device's x-axis.

Value
-----

A `Number`.

Examples
--------

 The magnetometer is typically read in the `reading` event
 callback. In the example below this occurs sixty times a second.

```js
let magSensor = new Magnetometer({ frequency: 60 });

magSensor.addEventListener("reading", (e) => {
  console.log(`Magnetic field along the X-axis ${magSensor.x}`);
  console.log(`Magnetic field along the Y-axis ${magSensor.y}`);
  console.log(`Magnetic field along the Z-axis ${magSensor.z}`);
});
magSensor.start();
```

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

| Specification |
| --- |
| Magnetometer # magnetometer-x |

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

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


Magnetometer: y property - Web APIs

Magnetometer: y property
========================

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

 The **`y`** read-only property of the
 `Magnetometer` interface returns a number specifying
 the magnetic field around the device's y-axis.

Value
-----

A `Number`.

Examples
--------

 The magnetometer is typically read in the `reading` event
 callback. In the example below this occurs sixty times a second.

```js
let magSensor = new Magnetometer({ frequency: 60 });

magSensor.addEventListener("reading", (e) => {
  console.log(`Magnetic field along the X-axis ${magSensor.x}`);
  console.log(`Magnetic field along the Y-axis ${magSensor.y}`);
  console.log(`Magnetic field along the Z-axis ${magSensor.z}`);
});
magSensor.start();
```

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

| Specification |
| --- |
| Magnetometer # magnetometer-y |

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

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


BlobEvent: timecode property - Web APIs

BlobEvent: timecode property
============================

The **`timecode`** read-only property of the `BlobEvent` interface indicates the difference between the timestamp of the first chunk of data, and the timestamp of the first chunk in the first `BlobEvent` produced by this recorder.

Note that the `timecode` in the first produced `BlobEvent` does not need to be zero.

Value
-----

A `DOMHighResTimeStamp`.

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

| Specification |
| --- |
| MediaStream Recording # dom-blobevent-timecode |

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

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


BlobEvent: BlobEvent() constructor - Web APIs

BlobEvent: BlobEvent() constructor
==================================

 The **`BlobEvent()`** constructor returns a newly created
 `BlobEvent` object with an associated `Blob`.

Syntax
------

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

### Parameters

`type`

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

`options`

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

`data`

The `Blob` associated with the event.

`timecode` Optional

A `DOMHighResTimeStamp` to be used in initializing the blob event.

### Return value

A new `BlobEvent` object.

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

| Specification |
| --- |
| MediaStream Recording # dom-blobevent-blobevent |

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

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

See also
--------

* The `BlobEvent` interface it belongs to.


BlobEvent: data property - Web APIs

BlobEvent: data property
========================

The **`data`** read-only property of the `BlobEvent` interface represents a `Blob` associated with the event.

Value
-----

A `Blob` object.

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

| Specification |
| --- |
| MediaStream Recording # dom-blobevent-data |

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

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

See also
--------

* The `BlobEvent` interface it belongs to.


MediaSession: setActionHandler() method - Web APIs

MediaSession: setActionHandler() method
=======================================

 The **`setActionHandler()`** method of the `MediaSession` interface sets a handler for a media session action.
 These actions let a web app receive notifications when the user engages a device's built-in physical or onscreen media controls, such as play, stop, or seek buttons.

Syntax
------

```js
setActionHandler(type, callback)
```

### Parameters

`type`

 A string representing an action type to listen for. It will be one
 of the following:

`hangup`

End a call.

`nextslide`

Moves to the next slide, when presenting a slide deck.

`nexttrack`

Advances playback to the next track.

`pause`

Pauses playback of the media.

`play`

Begins (or resumes) playback of the media.

`previousslide`

Moves to the previous slide, when presenting a slide deck.

`previoustrack`

Moves back to the previous track.

`seekbackward`

 Seeks backward through the media from the current position.
 The `seekOffset` property passed to the callback specifies the amount of time to seek backward.

`seekforward`

 Seeks forward from the current position through the media.
 The `seekOffset` property passed to the callback specifies the amount of time to seek forward.

`seekto`

 Moves the playback position to the specified time within the media.
 The time to which to seek is specified in the `seekTime` property passed to the callback.
 If you intend to perform multiple `seekto` operations in rapid succession, you can also specify the `fastSeek` property passed to the callback with a value of `true`.
 This lets the browser know it can take steps to optimize repeated operations, and is likely to result in improved performance.

`skipad`

 Skips past the currently playing advertisement or commercial.
 This action may or may not be available, depending on the platform and user agent, or may be disabled due to subscription level or other circumstances.

`stop`

Halts playback entirely.

`togglecamera`

Turn the user's active camera on or off.

`togglemicrophone`

Mute or unmute the user's microphone.

`callback`

A function to call when the specified action type is invoked. The callback should not return a value. The callback receives a dictionary containing the following properties:

`action`

A string representing the action type. This property allows a single callback to handle multiple action types.

`fastSeek` Optional

 A `seekto` action may *optionally* include this property, which is a Boolean value indicating whether or not to perform a "fast" seek.
 A "fast" seek is a seek being performed in a rapid sequence, such as when fast-forwarding or reversing through the media, rapidly skipping through it.
 This property can be used to indicate that you should use the shortest possible method to seek the media.
 `fastSeek` is not included on the final action in the seek sequence in this situation.

`seekOffset` Optional

 If the `action` is either `seekforward` or `seekbackward` and this property is present, it is a floating point value which indicates the number of seconds to move the play position forward or backward.
 If this property isn't present, those actions should choose a reasonable default distance to skip forward or backward (such as 7 or 10 seconds).

`seekTime` Optional

If the `action` is `seekto`, this property must be present and must be a floating-point value indicating the absolute time within the media to move the playback position to, where 0 indicates the beginning of the media. This property is not present for other action types.

### Return value

None (`undefined`).

Description
-----------

To remove a previously-established action handler, call `setActionHandler()` again, specifying `null` as the `callback`.

The action handler receives as input a single parameter: an object which provides both the action type (so the same function can handle multiple action types), as well as data needed in order to perform the action.

Examples
--------

### Setting up action handlers for a music player

This example creates a new media session and assigns action handlers (which don't do anything) to it.

```js
if ("mediaSession" in navigator) {
  navigator.mediaSession.metadata = new MediaMetadata({
    title: "Unforgettable",
    artist: "Nat King Cole",
    album: "The Ultimate Collection (Remastered)",
    artwork: [
      {
        src: "https://dummyimage.com/96x96",
        sizes: "96x96",
        type: "image/png",
      },
      {
        src: "https://dummyimage.com/128x128",
        sizes: "128x128",
        type: "image/png",
      },
      {
        src: "https://dummyimage.com/192x192",
        sizes: "192x192",
        type: "image/png",
      },
      {
        src: "https://dummyimage.com/256x256",
        sizes: "256x256",
        type: "image/png",
      },
      {
        src: "https://dummyimage.com/384x384",
        sizes: "384x384",
        type: "image/png",
      },
      {
        src: "https://dummyimage.com/512x512",
        sizes: "512x512",
        type: "image/png",
      },
    ],
  });

  navigator.mediaSession.setActionHandler("play", () => {
    /\* Code excerpted. \*/
  });
  navigator.mediaSession.setActionHandler("pause", () => {
    /\* Code excerpted. \*/
  });
  navigator.mediaSession.setActionHandler("stop", () => {
    /\* Code excerpted. \*/
  });
  navigator.mediaSession.setActionHandler("seekbackward", () => {
    /\* Code excerpted. \*/
  });
  navigator.mediaSession.setActionHandler("seekforward", () => {
    /\* Code excerpted. \*/
  });
  navigator.mediaSession.setActionHandler("seekto", () => {
    /\* Code excerpted. \*/
  });
  navigator.mediaSession.setActionHandler("previoustrack", () => {
    /\* Code excerpted. \*/
  });
  navigator.mediaSession.setActionHandler("nexttrack", () => {
    /\* Code excerpted. \*/
  });
  navigator.mediaSession.setActionHandler("skipad", () => {
    /\* Code excerpted. \*/
  });
  navigator.mediaSession.setActionHandler("togglecamera", () => {
    /\* Code excerpted. \*/
  });
  navigator.mediaSession.setActionHandler("togglemicrophone", () => {
    /\* Code excerpted. \*/
  });
  navigator.mediaSession.setActionHandler("hangup", () => {
    /\* Code excerpted. \*/
  });
}
```

The following example sets up two functions for playing and pausing, then uses them as callbacks with the relevant action handlers.

```js
const actionHandlers = [
  // play
  [
    "play",
    async () => {
      // play our audio
      await audioEl.play();
      // set playback state
      navigator.mediaSession.playbackState = "playing";
      // update our status element
      updateStatus(allMeta[index], "Action: play | Track is playing…");
    },
  ],
  [
    "pause",
    () => {
      // pause out audio
      audioEl.pause();
      // set playback state
      navigator.mediaSession.playbackState = "paused";
      // update our status element
      updateStatus(allMeta[index], "Action: pause | Track has been paused…");
    },
  ],
];

for (const [action, handler] of actionHandlers) {
  try {
    navigator.mediaSession.setActionHandler(action, handler);
  } catch (error) {
    console.log(`The media session action "${action}" is not supported yet.`);
  }
}
```

This example uses appropriate action handlers to allow seeking in either direction through the playing media.

```js
navigator.mediaSession.setActionHandler("seekbackward", (evt) => {
  // User clicked "Seek Backward" media notification icon.
  let skipTime = evt.seekOffset || 10; // Time to skip in seconds
  audio.currentTime = Math.max(audio.currentTime - skipTime, 0);
});

navigator.mediaSession.setActionHandler("seekforward", (evt) => {
  // User clicked "Seek Forward" media notification icon.
  let skipTime = evt.seekOffset || 10; // Time to skip in seconds
  audio.currentTime = Math.min(audio.currentTime + skipTime, audio.duration);
});
```

To remove a media action handler, assign it to null.

```js
navigator.mediaSession.setActionHandler("nexttrack", null);
```

### Supporting multiple actions in one handler function

You can also, if you prefer, use a single function to handle multiple action types, by checking the value of the `action` property:

```js
let skipTime = 7;

navigator.mediaSession.setActionHandler("seekforward", handleSeek);
navigator.mediaSession.setActionHandler("seekbackward", handleSeek);

function handleSeek(details) {
  switch (details.action) {
    case "seekforward":
      audio.currentTime = Math.min(
        audio.currentTime + skipTime,
        audio.duration,
      );
      break;
    case "seekbackward":
      audio.currentTime = Math.max(audio.currentTime - skipTime, 0);
      break;
  }
}
```

Here, the `handleSeek()` function handles both `seekbackward` and `seekforward` actions.

### Using action handlers to control a slide presentation

The `"previousslide"` and `"nextslide"` action handlers can be used to handle moving forward and backward through a slide presentation, for example when the user puts their presentation into a Picture-in-Picture window, and presses the browser-supplied controls for navigating through slides.

```js
try {
  navigator.mediaSession.setActionHandler("previousslide", () => {
    log('> User clicked "Previous Slide" icon.');
    if (slideNumber > 1) slideNumber--;
    updateSlide();
  });
} catch (error) {
  log('Warning! The "previousslide" media session action is not supported.');
}

try {
  navigator.mediaSession.setActionHandler("nextslide", () => {
    log('> User clicked "Next Slide" icon.');
    slideNumber++;
    updateSlide();
  });
} catch (error) {
  log('Warning! The "nextslide" media session action is not supported.');
}
```

See Presenting Slides / Media Session Sample for a working example.

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

| Specification |
| --- |
| Media Session # dom-mediasession-setactionhandler |

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

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


MediaSession: setMicrophoneActive() method - Web APIs

MediaSession: setMicrophoneActive() method
==========================================

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

The **`setMicrophoneActive()`** method of the `MediaSession` interface is used to indicate to the user agent whether the user's microphone is considered to be currently muted.

 Call this method on the `navigator` object's
 `mediaSession` object.

Note that the status of the microphone is not tracked in the `MediaSession` itself, but must be tracked separately.

Syntax
------

```js
setMicrophoneActive(active)
```

### Parameters

`active`

A boolean indicating whether the microphone is considered muted or not.

### Return value

None (`undefined`).

Examples
--------

 Below is an example of updating the microphone mute state of the current
 `MediaSession`, as well as listening to requests to change the mute status with `setActionHandler()`.

```js
let microphoneActive = false;

navigator.mediaSession.setMicrophoneActive(microphoneActive);

navigator.mediaSession.setActionHandler("togglemicrophone", () => {
  microphoneActive = !microphoneActive;
  navigator.mediaSession.setMicrophoneActive(microphoneActive);
});
```

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

| Specification |
| --- |
| Media Session # dom-mediasession-setmicrophoneactive |

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

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


MediaSession: playbackState property - Web APIs

MediaSession: playbackState property
====================================

 The **`playbackState`** property of the
 `MediaSession` interface indicates whether the current media session is
 playing or paused.

Value
-----

 A string indicating the current playback state of the media session.
 The value may be one of the following:

`none`

 The browsing context doesn't currently know the current playback state, or the
 playback state is not available at this time.

`paused`

The browser's media session is currently paused. Playback may be resumed.

`playing`

The browser's media session is currently playing media, which can be paused.

Example
-------

 The following example sets up two functions for playing and pausing, then uses them as
 callbacks with the relevant action handlers. Each function harnesses the
 `playbackState` property to indicate whether the audio is playing or paused.

```js
const actionHandlers = [
  // play
  [
    "play",
    async () => {
      // play our audio
      await audioEl.play();
      // set playback state
      navigator.mediaSession.playbackState = "playing";
      // update our status element
      updateStatus(allMeta[index], "Action: play | Track is playing…");
    },
  ],
  [
    "pause",
    () => {
      // pause out audio
      audioEl.pause();
      // set playback state
      navigator.mediaSession.playbackState = "paused";
      // update our status element
      updateStatus(allMeta[index], "Action: pause | Track has been paused…");
    },
  ],
];

for (const [action, handler] of actionHandlers) {
  try {
    navigator.mediaSession.setActionHandler(action, handler);
  } catch (error) {
    console.log(`The media session action "${action}" is not supported yet.`);
  }
}
```

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

| Specification |
| --- |
| Media Session # dom-mediasession-playbackstate |

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

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


MediaSession: metadata property - Web APIs

MediaSession: metadata property
===============================

 The **`metadata`** property of the `MediaSession`
 interface contains a `MediaMetadata` object providing descriptive
 information about the currently playing media, or `null` if the metadata has
 not been set. This metadata is provided by the browser to the device for presentation in
 any standard media control user interface the device might offer.

Value
-----

 An instance of `MediaMetadata` containing information about the media
 currently being played.

Example
-------

 The following example checks for compatibility and creates a new media session with the
 relevant metadata:

```js
if ("mediaSession" in navigator) {
  navigator.mediaSession.metadata = new MediaMetadata({
    title: "Unforgettable",
    artist: "Nat King Cole",
    album: "The Ultimate Collection (Remastered)",
    artwork: [
      {
        src: "https://dummyimage.com/96x96",
        sizes: "96x96",
        type: "image/png",
      },
      {
        src: "https://dummyimage.com/128x128",
        sizes: "128x128",
        type: "image/png",
      },
      {
        src: "https://dummyimage.com/192x192",
        sizes: "192x192",
        type: "image/png",
      },
      {
        src: "https://dummyimage.com/256x256",
        sizes: "256x256",
        type: "image/png",
      },
      {
        src: "https://dummyimage.com/384x384",
        sizes: "384x384",
        type: "image/png",
      },
      {
        src: "https://dummyimage.com/512x512",
        sizes: "512x512",
        type: "image/png",
      },
    ],
  });
}
```

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

| Specification |
| --- |
| Media Session # dom-mediasession-metadata |

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

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


MediaSession: setPositionState() method - Web APIs

MediaSession: setPositionState() method
=======================================

 The **`setPositionState()`** method of the
 `MediaSession` interface is used to update the current
 document's media playback position and speed for presentation by user's device in any
 kind of interface that provides details about ongoing media. This can be
 particularly useful if your code implements a player for type of media not directly
 supported by the browser.

 Call this method on the `navigator` object's
 `mediaSession` object.

Syntax
------

```js
setPositionState()
setPositionState(stateDict)
```

### Parameters

`stateDict` Optional

 An object providing updated information about the playback position and speed
 of the document's ongoing media. If the object is empty, the existing playback
 state information is cleared. This object can contain the following
 parameters:

`duration` Optional

A floating-point value giving the total duration of the current media in seconds. This should always be a positive number, with positive infinity (`Infinity`) indicating media without a defined end, such as a live stream.

`playbackRate` Optional

A floating-point value indicating the rate at which the media is being played, as a ratio relative to its normal playback speed. Thus, a value of 1 is playing at normal speed, 2 is playing at double speed, and so forth. Negative values indicate that the media is playing in reverse; -1 indicates playback at the normal speed but backward, -2 is double speed in reverse, and so on.

`position` Optional

A floating-point value indicating the last reported playback position of the media in seconds. This must always be a positive value.

### Return value

None (`undefined`).

### Exceptions

`TypeError`

This error can occur in an array of circumstances:

* The specified object's `duration` is missing, negative, or `null`.
* Its `position` is either negative or greater than `duration`.
* Its `playbackRate` is zero.

Examples
--------

 Below is a function which updates the position state of the current
 `MediaSession` track.

```js
function updatePositionState() {
  navigator.mediaSession.setPositionState({
    duration: audioEl.duration,
    playbackRate: audioEl.playbackRate,
    position: audioEl.currentTime,
  });
}
```

We can use this function when updating `media session
 metadata` and within callbacks for actions, such as below.

```js
navigator.mediaSession.setActionHandler("seekbackward", (details) => {
  // our time to skip
  const skipTime = details.seekOffset || 10;

  // set our position
  audioEl.currentTime = Math.max(audioEl.currentTime - skipTime, 0);
  updatePositionState();
});
```

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

| Specification |
| --- |
| Media Session # dom-mediasession-setpositionstate |

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

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


MediaSession: setCameraActive() method - Web APIs

MediaSession: setCameraActive() method
======================================

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

The **`setCameraActive()`** method of the `MediaSession` interface is used to indicate to the user agent whether the user's camera is considered to be active.

 Call this method on the `navigator` object's
 `mediaSession` object.

Note that the status of the camera is not tracked in the `MediaSession` itself, but must be tracked separately.

Syntax
------

```js
setCameraActive(active)
```

### Parameters

`active`

A boolean indicating whether the camera is considered active or not.

### Return value

None (`undefined`).

Examples
--------

 Below is an example of updating the camera active state of the current
 `MediaSession`, as well as listening to requests to change the camera status with `setActionHandler()`.

```js
let cameraActive = false;

navigator.mediaSession.setCameraActive(cameraActive);

navigator.mediaSession.setActionHandler("togglecamera", () => {
  cameraActive = !cameraActive;
  navigator.mediaSession.setCameraActive(cameraActive);
});
```

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

| Specification |
| --- |
| Media Session # dom-mediasession-setcameraactive |

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

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