Docs

Documentation versions (currently viewingVaadin 25.2 (pre-release))

Wake Lock API

Using the Wake Lock API to keep the device screen on from the server.

The WakeLock API gives server-side Java code access to the browser Screen Wake Lock API. It keeps the screen from dimming or locking while the lock is held — useful for dashboards left on display, kiosks, presentations, navigation, media playback, and similar always-on views. It exposes two static entry points — WakeLock.request() to acquire and WakeLock.release() to release — and a pair of reactive signals for observing state and availability.

Note
The Screen Wake Lock API requires a secure context (HTTPS), except on localhost during development. Safari supports it from version 16.4. The browser may also release the lock at any time — for example, when the tab becomes hidden, on low battery, or under operating-system power-saving rules — and the application has no control over that. Use WakeLock.availabilitySignal() to gate UI controls up front and WakeLock.activeSignal() to react to spontaneous releases.

Acquiring a Wake Lock

Use WakeLock.request() to ask the browser to keep the screen on. The call is asynchronous and fire-and-forget: by the time the method returns the browser has not necessarily granted the lock yet. Observe WakeLock.activeSignal() for the actual state — see Observing Active State.

Source code
Java
Button keepOn = new Button("Keep screen on", e -> WakeLock.request());

The framework adds two conveniences on top of the raw browser API:

  • The browser releases the lock automatically when the tab becomes hidden. The client transparently re-acquires it when the tab becomes visible again, so a single request() call covers the lifetime of the view; no visibilitychange listener is needed.

  • Calling request() while a lock is already held is a no-op.

For persistent failures — the API is unavailable, the origin is insecure, or the browser refuses with NotAllowedError — there is a request(onError) overload covered in Handling Errors.

Releasing a Wake Lock

Call WakeLock.release() to drop the current lock and stop re-acquiring it on subsequent visibility changes. The call is idempotent — calling it when no lock is held is a no-op.

Source code
Java
Button stop = new Button("Allow screen off", e -> WakeLock.release());

Observing Active State

WakeLock.activeSignal() returns a read-only Signal<Boolean> that reflects whether the browser is currently holding the wake lock for this UI. It starts as false, flips to true once the browser confirms a request, and flips back to false whenever the browser releases the lock — explicitly through release(), automatically when the tab becomes hidden, or because the browser dropped it for power-saving or low-battery reasons. When the tab is shown again the client re-requests the lock if release() has not been called, so the signal flips back to true shortly after.

Use Signal.effect() to react to changes — for example, to reflect the wake-lock state in the browser’s document title so a hidden tab makes its stay-on mode obvious:

Source code
Java
Signal.effect(this, () -> {
    Page page = UI.getCurrent().getPage();
    page.setTitle(WakeLock.activeSignal().get()
            ? "Live Dashboard (stay-on)"
            : "Live Dashboard");
});

When a component property accepts a signal directly, prefer that binding over Signal.effect(). The canonical "Start" / "Stop" toggle pair binds the signal to button visibility:

Source code
Java
Button keepOn = new Button("Keep screen on", e -> WakeLock.request());
keepOn.bindVisible(Signal.not(WakeLock.activeSignal()));

Button allowOff = new Button("Allow screen off", e -> WakeLock.release());
allowOff.bindVisible(WakeLock.activeSignal());

Handling Errors

Pass a SerializableConsumer<WakeLockError> to WakeLock.request() to be notified when the browser permanently refuses the request. The callback fires on the UI thread. Switch on err.code() to get a typed WakeLockErrorCode — the switch is exhaustive at compile time:

Source code
Java
WakeLock.request(err -> {
    String userMessage = switch (err.code()) {
        case UNSUPPORTED ->
            "Keep-screen-on isn't available in this browser.";
        case NOT_ALLOWED ->
            "The browser refused to keep the screen on.";
        case UNKNOWN ->
            "Couldn't keep the screen on.";
    };
    Notification.show(userMessage);
});

The callback is meant for persistent failures — the kinds that should surface in the UI, typically by re-enabling a "Keep screen on" toggle and showing a message. It is not invoked when the lock is merely deferred (tab hidden until the user returns) or when the browser temporarily releases the lock for power-management reasons; observe activeSignal() for those.

When availability is already known to be UNSUPPORTED (see Checking Availability), the error callback fires synchronously, without a server-to-client round-trip. This makes it cheap to wire the toggle up unconditionally and rely on the error callback to degrade gracefully on insecure origins.

For diagnostics, log err.message() alongside the code — it is a free-form browser string and isn’t meant for end users.

Checking Availability

WakeLock.availabilitySignal() returns a Signal<WakeLockAvailability> reporting whether the Screen Wake Lock API is usable in the current page context. Reading the signal does not call the browser API — it reports whether a subsequent request() has any chance of succeeding.

SUPPORTED

The browser exposes the API and the page is served from a secure context. Render the "keep screen on" toggle.

UNSUPPORTED

The browser does not implement the API, or the page is served over an insecure connection. No user action changes this — hide the toggle entirely.

UNKNOWN

The browser has not yet reported a value. This is the seed state during the brief window between UI attach and bootstrap completion; in practice applications see it only as a transient state.

Use bindVisible() to render the toggle only when the API is usable:

Source code
Java
Button keepOn = new Button("Keep screen on", e -> WakeLock.request());
keepOn.bindVisible(WakeLock.availabilitySignal()
        .map(a -> a == WakeLockAvailability.SUPPORTED));

Calling from Background Threads

The static entry points above resolve the target UI through UI.getCurrent(), which only works on a request-handling thread. Background threads (executors, scheduled tasks, push handlers) need to pass the UI explicitly. Every entry point has an overload accepting a UI argument: request(ui), request(onError, ui), release(ui), activeSignal(ui), and availabilitySignal(ui).

Source code
Java
@Override
protected void onAttach(AttachEvent event) {
    UI ui = event.getUI();
    ScheduledFuture<?> task = scheduler.schedule(
            () -> WakeLock.request(ui),
            5, TimeUnit.SECONDS);
    addDetachListener(e -> task.cancel(false));
}

The error callback on request(onError, ui) is still invoked on the UI thread, so it can update components directly without an ui.access() wrapper.

Limitations and Caveats

  • A secure context is required (HTTPS, or localhost during development).

  • Safari supports the API from 16.4; older versions report UNSUPPORTED through availabilitySignal().

  • The browser may release the lock at any time — low battery, power-saving mode, or operating-system policy. activeSignal() reflects the change so the UI can react.

  • Wake lock state is per-UI (per tab). Each tab needs its own request(); releasing in one tab does not affect another.