recursal/MDN huggingface dataset:
Connecting the peers
====================

* Previous
* Next

In the last article we set up our server, but it doesn't do anything yet because we are not serving anything. This is the part you've been waiting for — actually creating the client-side peer connection and call logic. This is going to be an involved process, but we've split it into numerous subsections so you can tackle the different parts in easy bite-sized chunks.

1. First up, create a `script.js` file in the same location as the others — this is where all your logic will live.
2. We need to create a peer object with an ID. The ID will be used to connect two peers together and if you don't create one, one will be assigned to the peer. Add the following to `script.js`:

```js
const peer = new Peer(
  `${Math.floor(Math.random() \* 2 \*\* 18)
 .toString(36)
 .padStart(4, 0)}`,
  {
    host: location.hostname,
    debug: 1,
    path: "/myapp",
  },
);
```

3. You'll then need to attach the peer to the window so that it's accessible. Add the following line below your previous code:

```js
window.peer = peer;
```

4. In another terminal window, start the peer server by running the following command inside the root of your phone app directory:

```bash
peerjs --port 443 --key peerjs --path /myapp
```


This looks very similar to the peer server we created in the last step; this is the client-side portion. In order for the browser to connect to the running peer server, we need to tell it how; this is what the above line does.

* Previous
* Next
Setup
=====

* Previous
* Next

So let's get started by setting up the basis for our WebRTC-powered phone app.

1. First find a sensible place on your local file structure and run `mkdir audio_app` and then `cd audio_app` to create a directory to contain your app and enter into it.
2. Next, create a new app by running `yarn init`. Follow the prompts, providing a name, version, description, etc. to your project.
3. Next, install the required dependencies using the following commands:
    * Express: `yarn add express`
    * PeerJS: `yarn add peerjs`
    * Peer: `yarn add peer`Peer will be used for the peer server and PeerJS will be used to access the PeerJS API and framework. Your `package.json` should something look like this when you've finished installing the dependencies:

```json
{
  "name": "audio\_app",
  "version": "1.0.0",
  "description": "An audio app using WebRTC",
  "scripts": {
    "start": "node server.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "Lola Odelola",
  "license": "MIT",
  "dependencies": {
    "express": "^4.17.1",
    "peer": "^0.5.3",
    "peerjs": "^1.3.1"
  }
}
```

4. To finish the setup, you'll want to copy the following HTML and CSS files into the root of your project folder. You can name both files 'index', so the HTML file will be 'index.html' and the CSS file will be 'index.css'. You won't need to modify these much in the articles that follow.
* Previous
* Next
Creating a Call
===============

* Previous
* Next

Exciting times — now you're going to give your users the ability to create calls.

1. First of all, get a reference to the "Call" button that's defined in the HTML, by adding the following to the bottom of `script.js`:

```js
const callBtn = document.querySelector(".call-btn");
```

2. When a caller clicks "Call" you'll want to ask them for the peer ID of the peer they want to call (which we will store in the `code` variable in `getStreamCode()`) and then you'll want to create a connection with that code. Add the following below your previous code:

```js
callBtn.addEventListener("click", () => {
  getStreamCode();
  connectPeers();
  const call = peer.call(code, window.localStream); // A

  call.on("stream", (stream) => {
    // B
    window.remoteAudio.srcObject = stream; // C
    window.remoteAudio.autoplay = true; // D
    window.peerStream = stream; //E
    showConnectedContent(); //F });
  });
});
```

Let's walk through this code:
    * `const call = peer.call(code, window.localStream)`: This will create a call with the `code` and `window.localStream` we've previously assigned. Note that the `localStream` will be the user's `localStream`. So for caller A it'll be their stream & for B, their own stream.
    * `call.on('stream', (stream) => {` : peerJS gives us a `stream` event which you can use on the `call` that you've created. When a call starts streaming, you need to ensure that the remote stream coming from the call is assigned to the correct HTML elements and window, this is where you'll do that.
    * The anonymous function takes a `MediaStream` object as an argument, which you then have to set to your window's HTML like you've done before. Here we get your remote `<audio>` element and assign the stream passed to the function to the `srcObject` property.
    * Ensure the element's `autoplay` attribute is also set to `true`.
    * Ensure that the window's `peerStream` is set to the stream passed to the function.
    * Finally you want to show the correct content, so call the `showConnectedContent()` function you created earlier.
3. To test this out, open `localhost:8000` in two browser windows and click Call inside one of them. You should see this:

 ![Two screens side by side both A cream background with the words 'phone a friend' in bold, dark green font as the heading. The first screen has 'Your device ID is: 3b77' and the second 'Your device ID is: 2doa', is immediately below the title and 'please use headphones!' below that. Following on, a big dark green button with 'Call' written in the same cream color of the background. The second screen has a browser dialogue that asks for a peer id.](/en-US/docs/Web/API/WebRTC_API/Build_a_phone_with_peerjs/Connect_peers/Creating_a_call/screens_side_by_side.png)
 If you submit the other peer's ID, the call will be connected!

This is all working so far, but we need to give the other browser the chance to answer or decline the call We'll do that next.

* Previous
* Next
Showing and hiding HTML
=======================

* Previous
* Next

Alright, so you've got the microphone permissions set up. The next step is to make sure each user knows what their peer ID is so that they can make connections. The peerJS framework gives us a bunch of event listeners we can call on the peer we created earlier on.

1. Let's use the `open` event to create a listener that displays the peer's ID when it is open. Add the following code to the bottom of `script.js`:

```js
peer.on("open", () => {
  window.caststatus.textContent = `Your device ID is: ${peer.id}`;
});
```

Here you're replacing the text in the HTML element with the ID `caststatus`.
2. Try reloading the app in your browser. Instead of `connecting...`, you should see `Your device ID is: <peer ID>`.

 ![A cream background with the words 'phone a friend' in bold, dark green font as the heading. 'Your device ID is: 3b77' is immediately below that and 'please use headphones!' below that. Following on, a big dark green button with 'Call' written in the same cream color of the background.](/en-US/docs/Web/API/WebRTC_API/Build_a_phone_with_peerjs/Connect_peers/Show_hide_html/app_showing_device_id.png)
3. While you're here, you may as well create some functions to display and hide various content, which you'll use later. There are two functions you should create, `showCallContent()` and `showConnectedContent()`. These functions will be responsible for showing the call button and showing the hang up button and audio elements when appropriate.

```js
const audioContainer = document.querySelector(".call-container");

// Displays the call button and peer ID
function showCallContent() {
  window.caststatus.textContent = `Your device ID is: ${peer.id}`;
  callBtn.hidden = false;
  audioContainer.hidden = true;
}

// Displays the audio controls and correct copy
function showConnectedContent() {
  window.caststatus.textContent = "You're connected";
  callBtn.hidden = true;
  audioContainer.hidden = false;
}
```

* Previous
* Next
Creating a peer connection
==========================

* Previous
* Next

Next, you want to ensure your users have a way of connecting with their peers. In order to connect two peers, you'll need the peer ID for one of them.

1. Let's create a variable to contain the ID, and a function to request that the user enters it that we'll call later. Add this to the bottom of `script.js`:

```js
let code;
function getStreamCode() {
  code = window.prompt("Please enter the sharing code");
}
```

The `window.prompt()` method provides a convenient way of getting the relevant peer ID — you can use this when you want to collect the peerID needed to create the connection.
2. Using the peerJS framework, you'll want to connect the `localPeer` to the `remotePeer`. PeerJS gives us the `connect()` function, which takes a peer ID to connect to. Add this block below your previous code:

```js
let conn;
function connectPeers() {
  conn = peer.connect(code);
}
```

3. When a connection is created, let's use the PeerJS framework's `on('connection')` to set the remote peer's ID and open the connection. The function for this listener accepts a `connection` object which is an instance of the `DataConnection` object (which is a wrapper around WebRTC's `RTCDataChannel`); within this function you'll want to assign it to a variable. Again you'll want to create the variable outside of the function so that you can assign it later. Add the following below your previous code:

```js
peer.on("connection", (connection) => {
  conn = connection;
});
```

* Previous
* Next
GPURenderBundle: label property
===============================

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

 The **`label`** read-only property of the
 `GPURenderBundle` interface is a string providing a label that can be used to identify the object, for example in `GPUError` messages or console warnings.

This can be set by providing a `label` property in the descriptor object passed into the originating `GPURenderBundleEncoder.finish()` call, or you can get and set it directly on the `GPURenderBundle` object.

Value
-----

A string. If no label value has previously been set, getting the label returns an empty string.

Examples
--------

Setting and getting a label via `GPURenderBundle.label`:


```js
const renderBundle = renderBundleEncoder.finish();

renderBundle.label = "myrenderbundle";
console.log(renderBundle.label); // "myrenderbundle"
```


Setting a label via the originating `GPURenderBundleEncoder.finish()` call, and then getting it via `GPURenderBundle.label`:


```js
const renderBundle = renderBundleEncoder.finish({
  label: "myrenderbundle",
});

console.log(renderBundle.label); // "myrenderbundle"
```

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

| Specification |
| --- |
| WebGPU # dom-gpuobjectbase-label |

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

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

See also
--------

* The WebGPU API
ReadableStreamDefaultReader: ReadableStreamDefaultReader() constructor
======================================================================

 The **`ReadableStreamDefaultReader()`**
 constructor creates and returns a `ReadableStreamDefaultReader` object
 instance.


**Note:** You generally wouldn't use this constructor manually; instead,
 you'd use the `ReadableStream.getReader()` method.


Syntax
------


```js
new ReadableStreamDefaultReader(stream)
```

### Parameters

`stream`

The `ReadableStream` to be read.

### Return value

An instance of the `ReadableStreamDefaultReader` object.

### Exceptions

`TypeError`

 Thrown if the supplied `stream` parameter is not a `ReadableStream`,
 or it is already locked for reading by another reader.


Examples
--------

 In the following simple example, a previously-created custom
 `ReadableStream` is read using a `ReadableStreamDefaultReader`
 created using `getReader()`. (see our Simple random stream example for the full code). Each chunk is read sequentially and output to
 the UI, until the stream has finished being read, at which point we return out of the
 recursive function and print the entire stream to another part of the UI.


```js
function fetchStream() {
  const reader = stream.getReader();
  let charsReceived = 0;

  // read() returns a promise that resolves
  // when a value has been received
  reader.read().then(function processText({ done, value }) {
    // Result objects contain two properties:
    // done - true if the stream has already given you all its data.
    // value - some data. Always undefined when done is true.
    if (done) {
      console.log("Stream complete");
      para.textContent = result;
      return;
    }

    // value for fetch streams is a Uint8Array
    charsReceived += value.length;
    const chunk = value;
    let listItem = document.createElement("li");
    listItem.textContent = `Received ${charsReceived} characters so far. Current chunk = ${chunk}`;
    list2.appendChild(listItem);

    result += chunk;

    // Read some more, and call this function again
    return reader.read().then(processText);
  });
}
```

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

| Specification |
| --- |
| Streams Standard # ref-for-default-reader-constructor① |

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

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

See also
--------

* Streams API concepts
* Using readable streams
* `ReadableStream`
* `ReadableStreamDefaultController`
ReadableStreamDefaultReader: releaseLock() method
=================================================

The **`releaseLock()`** method of the `ReadableStreamDefaultReader` interface releases the reader's lock on the stream.

If the associated stream is errored when the lock is released, the reader will appear errored in that same way subsequently; otherwise, the reader will appear closed.

 If the reader's lock is released while it still has pending read requests then the promises returned by the reader's `ReadableStreamDefaultReader.read()` method are immediately rejected with a `TypeError`.
 Unread chunks remain in the stream's internal queue and can be read later by acquiring a new reader.

Syntax
------


```js
releaseLock()
```

### Parameters

None.

### Return value

None (`undefined`).

### Exceptions

`TypeError`

Thrown if the source object is not a `ReadableStreamDefaultReader`.

Examples
--------


```js
function fetchStream() {
  const reader = stream.getReader();

  // ...

  reader.releaseLock();

  // ...
}
```

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

| Specification |
| --- |
| Streams Standard # ref-for-default-reader-release-lock② |

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

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

See also
--------

* `ReadableStreamDefaultReader()` constructor
* Using readable streams
ReadableStreamDefaultReader: cancel() method
============================================

 The **`cancel()`** method of the
 `ReadableStreamDefaultReader` interface returns a `Promise` that resolves when the stream is canceled. Calling this method signals a loss of interest in the stream by a consumer.

 Cancel is used when you've completely finished with the stream and don't need any more
 data from it, even if there are chunks enqueued waiting to be read. That data is lost
 after cancel is called, and the stream is not readable any more. To read those chunks
 still and not completely get rid of the stream, you'd use
 `ReadableStreamDefaultController.close()`.


**Note:** If the reader is active, the
 `cancel()` method behaves the same as that for the associated stream
 (`ReadableStream.cancel()`).


Syntax
------


```js
cancel()
cancel(reason)
```

### Parameters

`reason` Optional

A human-readable reason for the cancellation. This value may or may not be used.

### Return value

 A `Promise`, which fulfills with the value given in the `reason`
 parameter.

### Exceptions

`TypeError`

 The source object is not a `ReadableStreamDefaultReader`, or the stream
 has no owner.


Examples
--------

 In the following simple example, a previously-created custom
 `ReadableStream` is read using a `ReadableStreamDefaultReader`
 created using `getReader()`. (this code is based on our Simple random stream example). Each chunk is read sequentially and output to the UI, until the
 stream has finished being read, at which point we return out of the recursive function
 and print the entire stream to another part of the UI.

 When the stream is done (`if (done)`), we run `reader.cancel()`
 to cancel the stream, signalling that we don't need to use it any more.


```js
function fetchStream() {
  const reader = stream.getReader();
  let charsReceived = 0;

  // read() returns a promise that resolves
  // when a value has been received
  reader.read().then(function processText({ done, value }) {
    // Result objects contain two properties:
    // done - true if the stream has already given you all its data.
    // value - some data. Always undefined when done is true.
    if (done) {
      console.log("Stream complete");
      reader.cancel();
      para.textContent = result;
      return;
    }

    // value for fetch streams is a Uint8Array
    charsReceived += value.length;
    const chunk = value;
    let listItem = document.createElement("li");
    listItem.textContent = `Received ${charsReceived} characters so far. Current chunk = ${chunk}`;
    list2.appendChild(listItem);

    result += chunk;

    // Read some more, and call this function again
    return reader.read().then(processText);
  });
}
```

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

| Specification |
| --- |
| Streams Standard # ref-for-generic-reader-cancel② |

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

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

See also
--------

* `ReadableStreamDefaultReader()` constructor
* Using readable streams
ReadableStreamDefaultReader: closed property
============================================

 The **`closed`** read-only property of the
 `ReadableStreamDefaultReader` interface returns a
 `Promise` that fulfills when the stream closes, or rejects if the
 stream throws an error or the reader's lock is released. This property enables you
 to write code that responds to an end to the streaming process.

Value
-----

A `Promise`.

Examples
--------

 In this snippet, a previously-created reader is queried to see if the stream has been
 closed. When it is closed, the promise fulfills and the message is logged to the
 console.


```js
reader.closed.then(() => {
  console.log("reader closed");
});
```

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

| Specification |
| --- |
| Streams Standard # ref-for-generic-reader-closed② |

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

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

See also
--------

* `ReadableStreamDefaultReader()` constructor
* Using readable streams
StereoPannerNode: pan property
==============================

The `pan` property of the `StereoPannerNode` interface is an a-rate `AudioParam` representing the amount of panning to apply. The value can range between `-1` (full left pan) and `1` (full right pan).

Value
-----

An a-rate `AudioParam` containing the panning to apply.

**Note:** Though the `AudioParam` returned is read-only, the value it represents is not.

Examples
--------

See `BaseAudioContext.createStereoPanner()` for example code.

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

| Specification |
| --- |
| Web Audio API # dom-stereopannernode-pan |

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

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

See also
--------

* Using the Web Audio API
StereoPannerNode: StereoPannerNode() constructor
================================================

The **`StereoPannerNode()`** constructor of the Web Audio API creates a new `StereoPannerNode` object which is an `AudioNode` that represents a simple stereo panner node that can be used to pan an audio stream left or right.

Syntax
------


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

### Parameters

`context`

A reference to an `AudioContext`.

`options` Optional

Options are as follows:

`pan`

 A floating point number in the range [-1,1] indicating the position of an `AudioNode` in an output image.
 The value -1 represents full left and 1 represents full right.
 The default value is `0`.


`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 `StereoPannerNode` object instance.

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

| Specification |
| --- |
| Web Audio API # dom-stereopannernode-stereopannernode |

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

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.
MediaStream: addTrack() method
==============================

 The **`addTrack()`** method of the `MediaStream` interface adds a new track to the
 stream. The track is specified as a parameter of type `MediaStreamTrack`.


**Note:** If the specified track is already in the stream's track set, this method has no
 effect.


Syntax
------


```js
addTrack(track)
```

### Parameters

`track`

A `MediaStreamTrack` to add to the stream.

### Return value

None (`undefined`).

Examples
--------

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

| Specification |
| --- |
| Media Capture and Streams # dom-mediastream-addtrack |

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

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

See also
--------

* `MediaStream`, the interface it belongs to.
MediaStream: getAudioTracks() method
====================================

 The **`getAudioTracks()`** method of the
 `MediaStream` interface returns a sequence that represents all the
 `MediaStreamTrack` objects in this
 stream's `track set` where `MediaStreamTrack.kind`
 is `audio`.

Syntax
------


```js
getAudioTracks()
```

### Parameters

None.

### Return value

 An array of `MediaStreamTrack` objects, one for each audio track contained
 in the stream. Audio tracks are those tracks whose `kind` property is `audio`. The array is empty if the stream contains no
 audio tracks.


**Note:** The order of the returned tracks is not defined by the
 specification and may, in fact, change from one call to `getAudioTracks()`
 to the next.


 Early versions of this API included a special `AudioStreamTrack` interface
 which was used as the type for each entry in the list of audio streams; however, this
 has since been merged into the main `MediaStreamTrack` interface.

Examples
--------

 This example gets a webcam's audio and video in a stream using
 `getUserMedia()`, attaches the stream to a
 `<video>` element, then sets a timer that, upon expiring, will stop the
 first audio track found on the stream.


```js
navigator.mediaDevices
  .getUserMedia({ audio: true, video: true })
  .then((mediaStream) => {
    document.querySelector("video").srcObject = mediaStream;
    // Stop the audio stream after 5 seconds
    setTimeout(() => {
      const tracks = mediaStream.getAudioTracks();
      tracks[0].stop();
    }, 5000);
  });
```

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

| Specification |
| --- |
| Media Capture and Streams # dom-mediastream-getaudiotracks |

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

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