Tutorial
Implementing Picture-in-Picture in Android TWAs
September 29, 2026 · 7 min read
When building media, streaming, or video conferencing tools, providing a seamless multi-tasking experience is essential. Users expect to be able to read an article, reply to a message, or browse another app while a video continues to play in a floating window. Inside an Android Trusted Web Activity, this capability is fully supported through the native integration of the Chromium engine and the Web Picture-in-Picture API. By utilising these modern web APIs, your converted PWA can command native system-level overlay windows just like a native Android app built with Kotlin or Java.
How Picture-in-Picture Works in a TWA
In a standard web browser, Picture-in-Picture allows users to detach a video element from the viewport into a floating window that stays on top of other browser tabs. When running inside a TWA on Android, the operating system translates this web-level request into a native Android Picture-in-Picture mode transaction. The system web engine communicates directly with the Android window manager, transforming the TWA wrapper's visual layer into a floating native window overlay.
Because the TWA is backed by Google Chrome or the system WebView, the rendering of the floating video, the playback controls, and the hardware-accelerated decoding are handled entirely by the browser engine. The native wrapper package generated by PWAtoApp contains the necessary manifest declarations to tell Android that the activity is allowed to enter Picture-in-Picture mode, removing the need for you to write native platform code.
Checking for Picture-in-Picture Support
Before attempting to trigger Picture-in-Picture, your web application must check if the API is supported by the user's current web engine and if the feature is enabled on the device. Not all Android system browsers support this API, and users can manually disable draw-over-other-apps permissions in their Android system settings.
To verify support, check for the presence of the API on the document object in your JavaScript code:
if ('pictureInPictureEnabled' in document) { console.log('Picture-in-Picture is supported'); }
It is also good practice to check if the specific video element is allowed to enter this mode. Developers can explicitly disable this feature on certain video tracks by adding the disablePictureInPicture attribute directly to the HTML tag, which is useful for short decorative loops or advertisement banners.
Implementing the Request in JavaScript
To enter Picture-in-Picture mode, your application must call the requestPictureInPicture() method on a valid HTML video element. Due to browser security models, this method must be triggered by a direct user interaction, such as a click or a tap event handler.
The following example demonstrates how to wire up a video element and a custom control button to toggle Picture-in-Picture mode dynamically:
const videoElement = document.getElementById('main-video');
const pipToggleButton = document.getElementById('pip-button');
pipToggleButton.addEventListener('click', async () => {
try {
if (document.pictureInPictureElement) {
await document.exitPictureInPicture();
} else {
await videoElement.requestPictureInPicture();
}
} catch (error) {
console.error('Picture-in-Picture transaction failed', error);
}
});When this code executes inside your TWA, the active video element leaves the web page layout and morphs into a native floating window on the user's Android desktop, complete with system-rendered play and pause buttons.
Handling Automatic Picture-in-Picture on App Minimisation
While a manual button is helpful, the gold standard for user experience is triggering Picture-in-Picture automatically when the user swipes up to return to the Android home screen or switches to another app. To achieve this, you can monitor the visibility state of your web page using the Page Visibility API.
When the user minimises the TWA, the visibilitychange event is fired on the document. If a video is actively playing, you can automatically request Picture-in-Picture before the system suspends the web view context:
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
if (videoElement.paused === false && 'pictureInPictureEnabled' in document) {
videoElement.requestPictureInPicture();
}
}
});
By implementing this listener, your TWA mimics native streaming applications. The transition from fullscreen application to a small, floating thumbnail happens seamlessly without the user needing to manually tap a button before navigating away.
Responding to Picture-in-Picture Events
Your web application must stay in sync with the state of the Picture-in-Picture window. If a user closes the floating window or returns to the main application view, your interface should adapt accordingly. The Web API provides two events that make tracking this simple:
- enterpictureinpicture: Fired when the video successfully transitions to the floating window. Use this to hide on-screen playback controls or show a placeholder in the main web layout.
- leavepictureinpicture: Fired when the video returns to the browser layout or when the user closes the window. Use this to resume normal layout presentation or pause the video if the window was closed.
These event listeners can be configured directly on the HTML video element:
videoElement.addEventListener('enterpictureinpicture', (event) => {
const pipWindow = event.pictureInPictureWindow;
console.log('Entered PiP window width: ' + pipWindow.width);
pipToggleButton.textContent = 'Exit Picture-in-Picture';
});
videoElement.addEventListener('leavepictureinpicture', () => {
pipToggleButton.textContent = 'Enter Picture-in-Picture';
});
Integrating with the Media Session API
To provide a polished user experience, you should pair the Picture-in-Picture API with the Media Session API. This API allows you to customise the metadata and playback controls shown on the Android lock screen, the system notification shade, and within the native Picture-in-Picture window overlay.
By defining the active media metadata, you ensure that the system display shows the correct track title, artist name, and album artwork. You can also specify action handlers for standard playback actions like play, pause, seek backwards, or seek forwards:
if ('mediaSession' in navigator) {
navigator.mediaSession.metadata = new MediaMetadata({
title: 'Streaming Live Event',
artist: 'PWA Webcast',
artwork: [{ src: 'https://example.com/logo.png', sizes: '512x512', type: 'image/png' }]
});
navigator.mediaSession.setActionHandler('play', () => videoElement.play());
navigator.mediaSession.setActionHandler('pause', () => videoElement.pause());
}
This tight integration ensures that your TWA functions smoothly on Android, offering a level of background and control control that is indistinguishable from standard native video players.
Ready to ship your Android app?
Paste your PWA URL, get a signed APK and a Google Play ready AAB in minutes.
Build my app