Implement Picture-in-Picture on the Web
Tags: webdev, javascript, experimental
Published: 2019-01-16 21:07:19

---
title: Implement Picture-in-Picture on the Web
cover_image: https://thepracticaldev.s3.amazonaws.com/i/qwe31vh9f0gjb3p0gtwb.png
published: true
description: How to implement Picture-in-Picture (PiP) on the web 
tags: webdev, javascript, experimental
---

One of my favourite things to do on the web is to implement and test out new and upcoming features. Today we are going to implement Picture-in-Picture.


## What is Picture-in-Picture ?

According to [W3C Picture-in-Picture Spec-](https://wicg.github.io/picture-in-picture/)

<blockquote>The specification intends to provide APIs to allow websites to create a floating video window always on top of other windows so that users may continue consuming media while they interact with other content sites, or applications on their device.</blockquote>

While in the PiP(Picture-in-Picture) mode the video is contained in a separate *mini* window that is always on top of other windows. This window stays visible even when the user agent is not visible.

-----------------------

## How to implement Picture-in-Picture ?

HTML -

``` html
<video id="videoElement" controls="true" src="demo.mp4"></video>

<!-- button will be used to toggle the PiP mode -->
<button id="togglePipButton">Toggle Picture-in-Picture Mode!</button> 
```

JavaScript -

### 1. Call `requestPictureInPicture()` on click of `togglePipButton` button element.
`requestPictureInPicture()` returns a promise.
When the promise resolves, the browser will shrink the video into a mini window that the user can move around and position over other windows.

``` javascript
let video = document.getElementById('videoElement');
let togglePipButton = document.getElementById('togglePipButton');

togglePipButton.addEventListener('click', async function (event) {
    togglePipButton.disabled = true; //disable toggle button while the event occurs
    try {
        // If there is no element in Picture-in-Picture yet, request for it
        if (video !== document.pictureInPictureElement) {
            await video.requestPictureInPicture();
        }
        // If Picture-in-Picture already exists, exit the mode
        else {
            await document.exitPictureInPicture();
        }

    } catch (error) {
        console.log(`Oh Horror! ${error}`);
    } finally {
        togglePipButton.disabled = false; //enable toggle button after the event
    }
});
```


### 2. Check for Picture-in-Picture event changes

```javascript
let video = document.getElementById('videoElement');
video.addEventListener('enterpictureinpicture', function (event) {
    console.log('Entered PiP');
    pipWindow = event.pictureInPictureWindow;
    console.log(`Window size -  \n Width: ${pipWindow.width} \n Height: ${pipWindow.height}`); // get the width and height of PiP window
});

video.addEventListener('leavepictureinpicture', function (event) {
    console.log('Left PiP');
    togglePipButton.disabled = false;
});
```

We can even update video size based on Picture-in-Picture window size changes by adding a `resize` event handler. This will be helpful for serving the right quality of video based on the window the user is viewing it on.


### 3. Always check for feature support

```javascript
if ('pictureInPictureEnabled' in document) {
    showPipButton();

    // loadedmetadata event occurs when meta data for the specified audio/video has been loaded.Meta data might consists of: duration, dimensions etc.
    video.addEventListener('loadedmetadata', showPipButton);

    // emptied event is fired when the media has become empty, e.g. media has already been loaded or partially loaded.
    video.addEventListener('emptied', showPipButton);
} else {
    console.log('The Picture-in-Picture Web API is not available.');
    togglePipButton.hidden = true;
}

// Enable/disable toggle button depending on whether PiP availability.
function showPipButton() {
    togglePipButton.disabled = (video.readyState === 0) || !document.pictureInPictureEnabled || video.disablePictureInPicture;
}
// video.readyState === 0 means Video metadata have not been loaded yet 
// !document.pictureInPictureEnabled means if Pip is not available
// video.disablePictureInPicture means disablePictureInPicture attribute is present in the video which will result in requestPictureInPicture() rejected promise.
```

That's it! Your web app is now ready to use Picture-in-Picture!

--------------------

*Bonus!* Made a quick demo for this, [check it out!](https://ananyaneogi.github.io/picture-in-picture-demo/)

{% github https://github.com/ananyaneogi/picture-in-picture %}

Currently the API supports only on the `HTMLVideoElement` but it states to be extensible in the future.
Check the [caniuse stats](https://caniuse.com/#feat=picture-in-picture) for browser support. Since this is a progressive enhancement, so regardless of the support, you can use it today on your existing apps! Yay!

-------------------

## References
1. [W3C Picture-in-Picture Spec](https://wicg.github.io/picture-in-picture/)
2. [Building Modern Web Media Experiences(Chrome Dev Summit 2018)](https://www.youtube.com/watch?v=iTC3mfe0DwE)