DEV Community

Cover image for How I Built Pop-Upz: An Always-on-Top Floating Web Player Using Chrome's Document PiP API
Arunmozhi Varman
Arunmozhi Varman

Posted on

How I Built Pop-Upz: An Always-on-Top Floating Web Player Using Chrome's Document PiP API

Introduction

Have you ever wanted to keep an eye on a chat, check a stock ticker, monitor a build status board, or scroll through Hacker News while working inside other applications?

Typically, we are forced to resize multiple windows or use second monitors. While browsers support Picture-in-Picture (PiP), the traditional PiP API is strictly limited to <video> elements.

To solve this, I built Pop-Upz—a lightweight Chrome extension that detaches any webpage into a fully borderless, floating, always-on-top window.

In this post, I’ll walk through how I utilized the modern Document Picture-in-Picture API and bypassed cross-origin security restrictions to keep any website floating over your desktop.


🛠️ The Core Technology: Document Picture-in-Picture API

Introduced in Chrome 116, the Document Picture-in-Picture API makes it possible to open an always-on-top window that can be populated with arbitrary HTML content.

Unlike video-only PiP, this opens up endless utility. We can inject an <iframe> inside the PiP window to render an entire website.

Here is the basic framework to launch a Document PiP window from a user gesture:

// Request the Picture-in-Picture Window
const pipWindow = await window.documentPictureInPicture.requestWindow({
  width: 800,
  height: 600
});

// Populate the PiP window's document body
pipWindow.document.write(`
  <html>
    <body>
      <iframe src="https://example.com" style="width:100%; height:100%; border:none;"></iframe>
    </body>
  </html>
`);
pipWindow.document.close();
Enter fullscreen mode Exit fullscreen mode

🛑 The Challenge: Bypassing CSP and Frame Restrictions

If you try to load major websites (like YouTube, GitHub, or LinkedIn) inside an iframe, you will immediately run into console errors:

Refused to display 'https://...' in a frame because it set 'X-Frame-Options' to 'sameorigin'.
Enter fullscreen mode Exit fullscreen mode

Websites use headers like X-Frame-Options and Content-Security-Policy (CSP) to prevent clickjacking attacks by forbidding their pages from being embedded in iframes on other domains.

The Solution: Manifest V3 declarativeNetRequest

To bypass this limitation for our floating player, we can leverage Chrome's declarative rules to strip these blocking headers only when the iframe is requested inside our extension's subframe context.

Here is the ruleset configuration (rules.json):

[
  {
    "id": 1,
    "priority": 1,
    "action": {
      "type": "modifyHeaders",
      "responseHeaders": [
        { "header": "X-Frame-Options", "operation": "remove" },
        { "header": "Frame-Options", "operation": "remove" },
        { "header": "Content-Security-Policy", "operation": "remove" }
      ]
    },
    "condition": {
      "resourceTypes": ["sub_frame"]
    }
  }
]
Enter fullscreen mode Exit fullscreen mode

By removing these headers dynamically at the browser engine level, we can frame any website seamlessly inside our floating window.


⚡ Solving the Chrome Transient Activation Bug

One of the trickiest parts of building this extension was handling Chrome's User Gesture requirement. The browser prevents scripts from launching pop-ups programmatically unless triggered by an explicit click.

If you have any asynchronous delay (await statements or microtask yields) between the user's click and the requestWindow() call, Chrome immediately invalidates the gesture token, throwing this error:

"Could not launch Picture-in-Picture window. Please interact with the page first."

To solve this, I designed Pop-Upz to run the PiP instantiation completely synchronously inside the button click handler, only running the document generation logic inside the resolving .then() promise block:

// Synchronously bound to the click event to preserve transient gesture
window.documentPictureInPicture.requestWindow({
  width: 800,
  height: 600
})
.then((pipWindow) => {
  // Safe to write document asynchronously here!
  pipWindow.document.write(...);
  pipWindow.document.close();
});
Enter fullscreen mode Exit fullscreen mode

🎨 Clean and Minimalist Design

Instead of adding heavy settings menus or floating control bars that clutter the screen, Pop-Upz detaches into a 100% borderless, full-bleed viewport.

Users can trigger it in two ways:

  1. Clicking the Pop-Upz Toolbar Icon
  2. Right-clicking anywhere on a webpage and selecting "Pop Out with Pop-Upz ✦"

Once detached, you can drag the corners of the native window to resize it to any layout you prefer (Mobile view, Tablet view, Wide, etc.).


🚀 Check Out the Project

The project is fully open-source and ready to load as a developer unpacked extension.

Feel free to clone the repo, try it out, and star the project if you find it useful! Let me know in the comments how you plan to use always-on-top floating web pages in your workflow.

Top comments (0)