Picture-in-Picture for any Element, not just <video>  |  Web Platform  |  Chrome for Developers (2024)

Picture-in-Picture for any Element, not just <video> | Web Platform | Chrome for Developers (1)

François Beaufort

The Document Picture-in-Picture API makes it possible to open an always-on-top window that can be populated with arbitrary HTML content. It extends the existing Picture-in-Picture API for <video> that only allows an HTML <video> element to be put into a Picture-in-Picture window.

The Picture-in-Picture window in the Document Picture-in-Picture API is similar to a blank same-origin window opened via window.open(), with some differences:

  • The Picture-in-Picture window floats on top of other windows.
  • The Picture-in-Picture window never outlives the opening window.
  • The Picture-in-Picture window cannot be navigated.
  • The Picture-in-Picture window position cannot be set by the website.
Picture-in-Picture for any Element, not just <video> | Web Platform | Chrome for Developers (2)

Current status

StepStatus
1. Create explainerComplete
2. Create initial draft of specificationIn progress
3. Gather feedback & iterate on designIn progress
4. Origin trialComplete
5. LaunchComplete (Desktop)

Use cases

Custom video player

A website can provide a Picture-in-Picture video experience with the existing Picture-in-Picture API for <video>, however it is very limited. The existing Picture-in-Picture window accepts few inputs, and has limited ability for styling them. With a full Document in Picture-in-Picture, the website can provide custom controls and inputs (for example, captions, playlists, time scrubber, liking and disliking videos) to improve the user's Picture-in-Picture video experience.

Video conferencing

It is common for users to leave the browser tab during a video conferencing session for various reasons (for example, presenting another tab to the call or multitasking) while still wishing to see the call, so it's a prime use case for Picture-in-Picture. Once again, the current experience a video conferencing website can provide via the Picture-in-Picture API for <video> is limited in style and input. With a full Document in Picture-in-Picture, the website can easily combine multiple video streams into a single PiP window without having to rely on canvas hacks and provide custom controls such as sending a message, muting another user, or raising a hand.

Productivity

Research has shown that users need more ways to be productive on the web. Document in Picture-in-Picture gives web apps the flexibility to accomplish more. Whether it's text editing, note-taking, task lists, messaging and chat, or design and development tools, web apps can now keep their content always accessible.

Interface

Properties

documentPictureInPicture.window
Returns the current Picture-in-Picture window if any. Otherwise, returns null.

Methods

documentPictureInPicture.requestWindow(options)

Returns a promise that resolves when a Picture-in-Picture window is opened.The promise rejects if it's called without a user gesture.The options dictionary contains the optional following members:

width
Sets the initial width of the Picture-in-Picture window.
height
Sets the initial height of the Picture-in-Picture window.
disallowReturnToOpener
Hides the "back to tab" button in the Picture-in-Picture window if true. It is false by default.

Events

documentPictureInPicture.onenter
Fired on documentPictureInPicture when a Picture-in-Picture window is opened.

Examples

The following HTML sets up a custom video player and a button element to open the video player in a Picture-in-Picture window.

<div id="playerContainer"> <div id="player"> <video id="video"></video> </div></div><button id="pipButton">Open Picture-in-Picture window</button>

Open a Picture-in-Picture window

The following JavaScript calls documentPictureInPicture.requestWindow() when the user clicks the button to open a blank Picture-in-Picture window. The returned promise resolves with a Picture-in-Picture window JavaScript object. The video player is moved to that window using append().

pipButton.addEventListener('click', async () => { const player = document.querySelector("#player"); // Open a Picture-in-Picture window. const pipWindow = await documentPictureInPicture.requestWindow(); // Move the player to the Picture-in-Picture window. pipWindow.document.body.append(player);});

Set the size of the Picture-in-Picture window

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

pipButton.addEventListener("click", async () => { const player = document.querySelector("#player"); // Open a Picture-in-Picture window whose size is // the same as the player's. const pipWindow = await documentPictureInPicture.requestWindow({ width: player.clientWidth, height: player.clientHeight, }); // Move the player to the Picture-in-Picture window. pipWindow.document.body.append(player);});

Hide the "back to tab" button of the Picture-in-Picture window

To hide the button in the Picture-in-Picture window that allows the user to go back to the opener tab, set the disallowReturnToOpener option of documentPictureInPicture.requestWindow() to true.

pipButton.addEventListener("click", async () => { // Open a Picture-in-Picture window which hides the "back to tab" button. const pipWindow = await documentPictureInPicture.requestWindow({ disallowReturnToOpener: true, });});

Copy style sheets to the Picture-in-Picture window

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

pipButton.addEventListener("click", async () => { const player = document.querySelector("#player"); // Open a Picture-in-Picture window. const pipWindow = await documentPictureInPicture.requestWindow(); // Copy style sheets over from the initial document // so that the player looks the same. [...document.styleSheets].forEach((styleSheet) => { try { const cssRules = [...styleSheet.cssRules].map((rule) => rule.cssText).join(''); const style = document.createElement('style'); style.textContent = cssRules; pipWindow.document.head.appendChild(style); } catch (e) { const link = document.createElement('link'); link.rel = 'stylesheet'; link.type = styleSheet.type; link.media = styleSheet.media; link.href = styleSheet.href; pipWindow.document.head.appendChild(link); } }); // Move the player to the Picture-in-Picture window. pipWindow.document.body.append(player);});

Handle when the Picture-in-Picture window closes

Listen to the window "pagehide" event to know when the Picture-in-Picture window gets closed (either because the website initiated it or the user manually closed it). The event handler is a good place to get the elements back out of the Picture-in-Picture window as shown below.

pipButton.addEventListener("click", async () => { const player = document.querySelector("#player"); // Open a Picture-in-Picture window. const pipWindow = await documentPictureInPicture.requestWindow(); // Move the player to the Picture-in-Picture window. pipWindow.document.body.append(player); // Move the player back when the Picture-in-Picture window closes. pipWindow.addEventListener("pagehide", (event) => { const playerContainer = document.querySelector("#playerContainer"); const pipPlayer = event.target.querySelector("#player"); playerContainer.append(pipPlayer); });});

Close the Picture-in-Picture window programmatically by using the close() method.

// Close the Picture-in-Picture window programmatically. // The "pagehide" event will fire normally.pipWindow.close();

Listen to when the website enters Picture-in-Picture

Listen to the "enter" event on documentPictureInPicture to know when a Picture-in-Picture window is opened. The event contains a window object to access the Picture-in-Picture window.

documentPictureInPicture.addEventListener("enter", (event) => { const pipWindow = event.window;});

Access elements in the Picture-in-Picture window

Access elements in the Picture-in-Picture window either from the object returned by documentPictureInPicture.requestWindow(), or with documentPictureInPicture.window as shown below.

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

Handle events from the Picture-in-Picture window

Create buttons and controls and respond to user's input events such as "click" as you would do normally in JavaScript.

// Add a "mute" button to the Picture-in-Picture window.const pipMuteButton = pipWindow.document.createElement("button");pipMuteButton.textContent = "Mute";pipMuteButton.addEventListener("click", () => { const pipVideo = pipWindow.document.querySelector("#video"); pipVideo.muted = true;});pipWindow.document.body.append(pipMuteButton);

Resize the Picture-in-Picture window

Use the resizeBy() and resizeTo() Window methods to resize Picture-in-Picture window. Both methods require a user gesture.

const resizeButton = pipWindow.document.createElement('button');resizeButton.textContent = 'Resize';resizeButton.addEventListener('click', () => { // Expand the Picture-in-Picture window's width by 20px and height by 30px. pipWindow.resizeBy(20, 30);});pipWindow.document.body.append(resizeButton);

Focus the opener window

Use the focus() Window method to focus the opener window from the Picture-in-Picture window. This method requires a user gesture.

const returnToTabButton = pipWindow.document.createElement("button");returnToTabButton.textContent = "Return to opener tab";returnToTabButton.addEventListener("click", () => { window.focus();});pipWindow.document.body.append(returnToTabButton);

CSS picture-in-picture display mode

Use the CSS picture-in-picture display mode to write specific CSS rules that are only applied when (part of the) the web app is shown in Picture-in-Picture mode.

@media all and (display-mode: picture-in-picture) { body { margin: 0; } h1 { font-size: 0.8em; }}

Feature detection

To check if the Document Picture-in-Picture API is supported, use:

if ('documentPictureInPicture' in window) { // The Document Picture-in-Picture API is supported.}

Demos

VideoJS player

You can play with the Document Picture-in-Picture API VideoJS player demo. Be sure to check out the source code.

Pomodoro

Tomodoro, a pomodoro web app, is also taking advantage of the Document Picture-in-Picture API when available (see GitHub pull request).

Picture-in-Picture for any Element, not just <video> | Web Platform | Chrome for Developers (3)

Feedback

Please file issues on GitHub with suggestions and questions.

Useful links

Acknowledgements

Hero image by Jakob Owens.

Picture-in-Picture for any Element, not just <video>  |  Web Platform  |  Chrome for Developers (2024)

References

Top Articles
Latest Posts
Article information

Author: Carlyn Walter

Last Updated:

Views: 5953

Rating: 5 / 5 (70 voted)

Reviews: 93% of readers found this page helpful

Author information

Name: Carlyn Walter

Birthday: 1996-01-03

Address: Suite 452 40815 Denyse Extensions, Sengermouth, OR 42374

Phone: +8501809515404

Job: Manufacturing Technician

Hobby: Table tennis, Archery, Vacation, Metal detecting, Yo-yoing, Crocheting, Creative writing

Introduction: My name is Carlyn Walter, I am a lively, glamorous, healthy, clean, powerful, calm, combative person who loves writing and wants to share my knowledge and understanding with you.