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!

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