Picture-in-Picture made its first appearance on the web in the Safari browser with the release of macOS Sierra in 2016. It made it possible for a user to pop a video out into a small floating window that stays above all others, so that they can keep watching while doing other things. It’s an idea that came from TV, where, for example, you might want to keep watching your Popular Sporting Event even as you browse the guide or even other channels.
Not long after that, Android 8.0 was released which included picture-in-picture support via native APIs. Chrome for Android was able to play videos in picture-in-picture mode through this API even though its desktop counterpart was unable to do so.
This lead to the drafting of a standard Picture-in-Picture Web API which makes it possible for websites to initiate and control this behavior.
At the time of writing, only Chrome (version 70+) and Edge (version 76+) support this feature. Firefox, Safari, and Opera all use proprietary APIs for their implementations.
This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.
Desktop
Chrome | Firefox | IE | Edge | Safari |
---|---|---|---|---|
70 | 119 | No | 79 | 13.1 |
Mobile / Tablet
Android Chrome | Android Firefox | Android | iOS Safari |
---|---|---|---|
115 | 115 | No | 14.0-14.4 |
How to use the Picture-In-Picture API
Let’s start by adding a video to a webpage.
<video controls src="video.mp4"></video>
In Chrome, there will already be a toggle for entering and exiting Picture-in-Picture mode.
To test Firefox’s implementation, you’ll need to enable the media.videocontrols.picture-in-picture.enabled
flag in about:config
first, then right-click on the video to find the picture-in-picture option.
While this works, in many cases, you want your video controls to be consistent across browsers and you might want control over which videos can be entered into picture-in-picture mode and which ones cannot.
We can replace the default method of entering picture-in-picture mode in the browser with our own method using the Picture-in-Picture Web API. For example, let’s add a button that, when clicked, enables it:
<button id="pipButton" class="hidden" disabled>Enter Picture-in-Picture mode</button>
Then select both the video and the button in JavaScript:
const video = document.getElementById('video');
const pipButton = document.getElementById('pipButton');
The button is hidden and disabled by default because we need to know if the Picture-in-Picture API is supported and enabled in the user’s browser before displaying the it. This is a form of progressive enhancement which helps avoid a broken experience in browsers that do not support the feature.
We can check that the API is supported and enable the button as shown below:
if ('pictureInPictureEnabled' in document) {
pipButton.classList.remove('hidden')
pipButton.disabled = false;
}
Entering picture-in-picture mode
Let’s say our JavaScript has determined that the browser has picture-in-picture support enabled. Let’s call requestPictureInPicture()
on the video element when the button with #pipButton
is clicked. This method returns a promise that places the video in a mini window on the bottom-right side of the screen by default when resolved, although it can be moved around by the user.
if ('pictureInPictureEnabled' in document) {
pipButton.classList.remove('hidden')
pipButton.disabled = false;
pipButton.addEventListener('click', () => {
video.requestPictureInPicture();
});
}
We cannot leave the code above as is because requestPictureInPicture()
returns a promise and it’s possible for the promise to reject if, for example, the video metadata is not yet loaded or if the disablePictureInPicture
attribute is present on the video.
Let’s add a catch
block to capture this potential error and let the user know what is going on:
pipButton.addEventListener('click', () => {
video
.requestPictureInPicture()
.catch(error => {
// Error handling
});
});
Exiting picture-in-picture mode
The browser helpfully provides a close button on the picture-in-picture window, which enables the window to be close when clicked. However, we can also provide another way to exit picture-in-picture mode as well. For example, we can make clicking our #pipButton
close any active picture-in-picture window.
pipButton.addEventListener('click', () => {
if (document.pictureInPictureElement) {
document
.exitPictureInPicture()
.catch(error => {
// Error handling
})
} else {
// Request Picture-in-Picture
}
});
One other situation where you might want to close the picture-in-picture window is when the video enters full-screen mode. Chrome already does this automatically without having to write any code.
Picture-in-Picture events
The browser allows us to detect when a video enters or leaves picture-in-picture mode. Since there are many ways picture-in-picture mode can be entered or exited, it is better to rely on event detection to update media controls.
The events are enterpictureinpicture
and leavepictureinpicture
which, as their names imply, fire when a video enters or exits picture-in-picture mode, respectively.
In our example, we need to update the #pipButton
label depending on whether the video is or is not currently in picture-in-picture mode.
Here’s the code that helps us achieve that:
video.addEventListener('enterpictureinpicture', () => {
pipButton.textContent = 'Exit Picture-in-Picture mode';
});
video.addEventListener('leavepictureinpicture', () => {
pipButton.textContent = 'Enter Picture-in-Picture mode';
});
Here’s a demo of that:
Customizing the picture-in-picture window
The browser shows a play/pause button in the picture-in-picture window by default except when the video is playing a MediaStream object (produced by a virtual video source such as a camera, video recording device, screen sharing service, or other hardware sources).
It’s also possible to add controls that go to the previous or next track straight from the picture-in-picture window:
navigator.mediaSession.setActionHandler('previoustrack', () => {
// Go to previous track
});
navigator.mediaSession.setActionHandler('nexttrack', () => {
// Go to next track
});
Displaying a webcam feed in a picture-in-picture window
Video meeting web applications could benefit from placing a webcam feed in picture-in-picture mode when a user switches back and forth between the app and other browser tabs or windows.
Here’s an example:
Disabling picture-in-picture on a video
If you do not want a video to pop out in a picture-in-picture window, you can add the disablePictureInPicture
attribute to it, like this:
<video disablePictureInPicture controls src="video.mp4"></video>
Wrapping up
That’s pretty much all you need to about the Picture-in-Picture Web API at this time! Currently, the API only supports the <video>
element but it is meant to be extensible to other elements as well.
Although browser support is spotty right now, you can still use it as a way to progressively enhance the video experience on your website.
Further reading
- Picture-in-Picture API Specification
- Watch video using Picture-in-Picture
- Picture-in-Picture Sample (Google Chrome on GitHub)
A nice sum up, though as an end user, I really hate this feature/design. Even when the video is not an ad, blocking text with floating elements is annoying and distracting.
you shared an amazing blog about web api. this blog provide so useful information about api. api is most useful for android applications.
Coincidentally I’ve just used this API earlier today to monitor my remote desktop: https://twitter.com/tomayac/status/1199605948295401472.
Does not work on Twitter
Picture in Picture opera