Skip to content
All posts
April 17, 20265 min read

Fullscreen vs Embedded Content: The Hidden UX Problem in Digital Signage

Some content refuses to render in embedded mode and demands fullscreen — and there's no URL parameter to fix it. Here's why it happens, what you can and can't control, and how to handle it.

Digital SignageUXWebEngineering
Share:

You deploy a departure board for a hotel lobby. On launch day, every display shows a popup: "Click here to enable fullscreen." On a locked kiosk device, nobody clicks anything. The popup sits there. The content never loads.

This is the fullscreen-vs-embedded problem. It's different from iframe blocking, harder to detect in QA, and impossible to fix purely on the client side.


Why Some Content Demands Fullscreen

Pages detect whether they're being embedded using JavaScript:

javascript
// Content page running this check
if (window.self !== window.top) {
  // We are inside an iframe or WebView
  // Show fullscreen prompt, block content, or redirect
}

code
window.top
is the topmost window in the frame hierarchy.
code
window.self
is the current window. If they're different, the page knows it's embedded.

This check is common in:

  • Third-party departure and arrival boards
  • Live event streaming pages
  • Public transit display feeds
  • Hotel property management system portals
  • Any content that was built for standalone browser use, not embedding

The content provider uses this to enforce their intended UX — they designed for fullscreen, and they're blocking anything else.

[!NOTE] This is intentional behavior by the content owner, not a bug. Unlike iframe headers, you cannot negotiate with JavaScript that's already running inside the page you don't control.


Real Case: BLT Hotel Departure Monitor

A property management client needed live departure information on lobby displays. The departure feed was served by a third-party transport data provider.

In a standalone browser: loads cleanly, fullscreen, auto-refreshes.

In our signage player WebView: JavaScript detected the embedded context, rendered a modal popup with a "Request Fullscreen" button, and blocked all content behind the overlay.

The modal wasn't dismissible programmatically — it required a user click with

code
userGesture: true
in the browser context.

On a kiosk device with no input attached: no click, no fullscreen, no content.

We tried three approaches before finding a working path:

ApproachResult
Inject JavaScript to dismiss the modalModal re-appeared on page refresh
Pass
code
?fullscreen=true
URL param
Provider didn't support it
Open in fullscreen WebView modeWebView fullscreen != browser fullscreen, check still triggered
Request embed-friendly endpoint from provider✅ Worked — they had a kiosk mode URL

The provider had a

code
/kiosk
URL variant that disabled all of these checks. It wasn't in their documentation. It took an email to their support team to find out.

[!TIP] Before assuming there's no solution, contact the content provider directly. Many have kiosk or embed-friendly endpoints they don't document publicly. Ask specifically: "Do you have an endpoint that disables fullscreen requirements and user interaction prompts?"


The Technical Mechanics

How the Fullscreen API Works

The Fullscreen API requires a user gesture to trigger. You cannot call

code
element.requestFullscreen()
from JavaScript without a click, tap, or keyboard event that originated from the user.

javascript
// This FAILS without a user gesture
document.documentElement.requestFullscreen();
// → DOMException: play() request was interrupted

// This WORKS (called from a click handler)
button.addEventListener('click', () => {
  document.documentElement.requestFullscreen();
});

This is a browser security restriction that exists in all major browsers and cannot be bypassed in a WebView. It was added specifically to prevent content from going fullscreen without user consent.

What "Fullscreen" Means in Signage Context

There are three different things that get called "fullscreen" in signage deployments:

ModeWhat It MeansUser Gesture Required?
Window maximizedApp fills the screen, no browser chromeNo
Browser fullscreen (F11)Browser is fullscreen, status bar goneNo for the app; depends for API
Fullscreen API (
code
requestFullscreen
)
Specific element fills the screenYes — user click required

Most signage kiosks achieve "looks fullscreen" via the first mode — the app window is maximized and the system UI is hidden by kiosk mode policy. But content that calls

code
requestFullscreen()
programmatically needs the user gesture, regardless of how the window is sized.


What You Can and Cannot Control

You Can Control:

  • Whether your player app window is fullscreen
  • Whether the Android system UI (status bar, nav bar) is hidden
  • Whether the device is locked to your app (kiosk mode)
  • WebView settings like
    code
    setMediaPlaybackRequiresUserGesture(false)
    (for video)
kotlin
// Android WebView: disable user gesture for media (but not fullscreen)
webView.settings.mediaPlaybackRequiresUserGesture = false

// Force WebView to use hardware acceleration
webView.setLayerType(View.LAYER_TYPE_HARDWARE, null)

You Cannot Control:

  • JavaScript running inside the page you're loading
  • The
    code
    window.self !== window.top
    check made by the content page
  • Whether
    code
    requestFullscreen()
    succeeds without a user gesture
  • Security headers from content origins you don't own

[!IMPORTANT] If your content requires user gestures and your kiosk has no input device attached, you have a fundamental incompatibility — not a configuration problem. Solve it at the content level, not the player level.


Handling It in Your Deployment Workflow

Pre-Deployment Content Audit

Before any deployment, test your content URLs in a simulated kiosk environment:

  1. Open the URL in an Android WebView on your target device
  2. Do not touch the screen after it loads
  3. Wait 30 seconds
  4. Is the content visible and correct? If yes, no user-gesture issue.

If a modal or prompt appears: you have a problem to solve before go-live, not after.

Content Classification

Track your content sources and their embed behavior:

code
content-registry:
  - url: "https://feeds.provider.com/departures"
    embed_mode: "standalone_only"   # needs fullscreen
    kiosk_url: "https://feeds.provider.com/departures/kiosk"
    
  - url: "https://our-cms.internal/display/lobby"
    embed_mode: "embeddable"        # works in WebView
    
  - url: "https://news-widget.partner.com/live"
    embed_mode: "iframe_blocked"    # X-Frame-Options: DENY
    workaround: "screenshot_mirror"

When There's No Fix

If the content provider won't give you a kiosk endpoint and the page demands user interaction, you have two real options:

  1. Screen mirroring: Run the content in a dedicated browser session, mirror that screen to your displays. High maintenance, but the content runs natively without embedding.

  2. Native rebuild: If the content is data-driven (departure times, schedules, prices), pull the data via their API and render it yourself. You own the UI, you own the behavior, no embedding required.

The embedding approach is always the first choice for speed. But some content was never designed to be embedded — and no amount of configuration will make it behave as if it was.

Know which category your content falls into before you commit to an architecture. It will save you from going live with a lobby full of popup-blocked displays.

Share:
S

Sudarshan Chaudhari

AI Systems Builder / Product Engineer

Bangkok, Thailand

Solo Android developer with 13+ years in QA, building Android apps, AI automation systems, and developer tools at SudarshanTechLabs.

Stay updated

Get new posts on Android, Kotlin, and solo dev straight to your inbox.

Newsletter preferences

Building something? Available for Android dev and QA consulting.

Work with me

Comments — powered by Giscus