How to enter Picture-in-Picture on background from inline playback in WKWebView

I'm building a Capacitor iOS app with a plain <video> element playing an MP4 file inline. I want Picture-in-Picture to activate automatically when the user goes home — swipe up from the bottom edge of the screen (on an iPhone with Face ID) or press the Home button (on an iPhone with a Home button).

Fullscreen → background works perfectly — iOS automatically enters Picture-in-Picture. But I need this to work from inline playback without requiring the user to enter fullscreen first.

Setup

<video id="video" playsinline autopictureinpicture controls
  src="http://podcasts.apple.com/resources/462787156.mp4">
</video>
// AppDelegate.swift
let audioSession = AVAudioSession.sharedInstance()
try? audioSession.setCategory(.playback, mode: .moviePlayback)
try? audioSession.setActive(true)
  • UIBackgroundModes: audio in Info.plist
  • allowsPictureInPictureMediaPlayback is true (Apple default)
  • iOS 26.3.1, WKWebView via Capacitor

What I've tried

1. autopictureinpicture attribute

<video playsinline autopictureinpicture ...>

WKWebView doesn't honor this attribute from inline playback. It only works when transitioning from fullscreen.

2. requestPictureInPicture() on visibilitychange

document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'hidden' && !video.paused) {
    video.requestPictureInPicture();
  }
});

Result: Fails with "not triggered by user activation". The visibilitychange event doesn't count as a user gesture.

3. webkitSetPresentationMode('picture-in-picture') on visibilitychange

document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'hidden' && !video.paused) {
    video.webkitSetPresentationMode('picture-in-picture');
  }
});

Result: No error thrown. The webkitpresentationmodechanged event fires with value picture-in-picture. But the PIP window never actually appears. The API silently accepts the call but nothing renders.

4. await play() then webkitSetPresentationMode

document.addEventListener('visibilitychange', async () => {
  if (document.visibilityState === 'hidden') {
    await video.play();
    video.webkitSetPresentationMode('picture-in-picture');
  }
});

Result: play() succeeds (audio resumes in background), but PIP still doesn't open.

5. Auto-resume on system pause + PIP on visibilitychange

iOS fires pause before visibilitychange when backgrounding. I tried resuming in the pause handler, then requesting PIP in visibilitychange:

video.addEventListener('pause', () => {
  if (document.visibilityState === 'hidden') {
    video.play(); // auto-resume
  }
});

document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'hidden' && !video.paused) {
    video.webkitSetPresentationMode('picture-in-picture');
  }
});

Result: Audio resumes successfully, but PIP still doesn't open.

6. Native JS eval from applicationDidEnterBackground

func applicationDidEnterBackground(_ application: UIApplication) {
    webView?.evaluateJavaScript(
        "document.querySelector('video').requestPictureInPicture()"
    )
}

Result: Same failure — no user activation context.

Observations

  • The event order on background is: pausevisibility: hidden
  • webkitSetPresentationMode reports success (event fires, no error) but the PIP window never renders
  • requestPictureInPicture() consistently requires user activation, even from native JS eval
  • Audio can be resumed in background via play(), but PIP is a separate gate
  • Fullscreen → background automatically enters Picture-in-Picture, confirming the WKWebView PIP infrastructure is functional

Question

Is there any way to programmatically enter PIP from inline playback when a WKWebView app goes to background? Or is this intentionally restricted by WebKit to fullscreen-only transitions?

Any pointers appreciated. Thanks!

Answered by DTS Engineer in 881409022
Is there any way to programmatically enter PIP from inline playback when a WKWebView app goes to background? Or is this intentionally restricted by WebKit to fullscreen-only transitions?

Without user interaction, such as a button press, transitioning to PiP automatically would require AVPlayerViewController to be implemented in the inline video.

The issue here is playsinline does not hand the video off to AVPlayerViewController.

Once the inline video uses AVPlayerViewController, you can implement automatic switching to PiP by enabling canStartPictureInPictureAutomaticallyFromInline.

This is mentioned in Adopting Picture in Picture in a Standard Player

As for your testing:

visibilitychange is not considered user interaction. Only begin PiP playback in response to user interaction and never programmatically. For example, webkitSetPresentationMode will trigger PiP if it is set as the action of a button. In the situations where the video doesn’t transition to PiP but the audio resumes:

When video.play() is called, background audio may be permitted via the audio session and is handled separately through the AVAudioSession set up on lines 11 and 12 of your AppDelegate.swift and UIBackgroundModes: audio in your Info.plist

Thank you for posting what you tried here, I hope this is useful for others who run into the same issue.

 Travis

Is there any way to programmatically enter PIP from inline playback when a WKWebView app goes to background? Or is this intentionally restricted by WebKit to fullscreen-only transitions?

Without user interaction, such as a button press, transitioning to PiP automatically would require AVPlayerViewController to be implemented in the inline video.

The issue here is playsinline does not hand the video off to AVPlayerViewController.

Once the inline video uses AVPlayerViewController, you can implement automatic switching to PiP by enabling canStartPictureInPictureAutomaticallyFromInline.

This is mentioned in Adopting Picture in Picture in a Standard Player

As for your testing:

visibilitychange is not considered user interaction. Only begin PiP playback in response to user interaction and never programmatically. For example, webkitSetPresentationMode will trigger PiP if it is set as the action of a button. In the situations where the video doesn’t transition to PiP but the audio resumes:

When video.play() is called, background audio may be permitted via the audio session and is handled separately through the AVAudioSession set up on lines 11 and 12 of your AppDelegate.swift and UIBackgroundModes: audio in your Info.plist

Thank you for posting what you tried here, I hope this is useful for others who run into the same issue.

 Travis

How to enter Picture-in-Picture on background from inline playback in WKWebView
 
 
Q