Run any website as a native, chromeless desktop app on Linux β built on Firefox's first-party "Web Apps" (Taskbar Tabs) infrastructure, with system-tray integration.
ffwebapps installs websites (PWAs or any site) as standalone apps: their own window with no tabs or address bar, their own taskbar/dock identity, a system-tray icon with an unread badge, close-to-tray / run-in-background, and out-of-scope links that open in your real browser.
It's a CLI-driven fork of PWAsForFirefox's native component, re-architected to drive Firefox's built-in Web Apps support instead of patching the browser chrome at runtime.
π nine7nine.github.io/ffwebapps β
architectural and technical reference, with diagrams: the runtime & autoconfig,
the IPC protocol and runtime-owned window, the data model, link routing, the
tray, the GTK GUI, desktop integration, performance tuning, and packaging. The
sources live in docs/ and build with cd docs && ./md2html.sh.
- Chromeless window β no tabs, no address bar; a dark, app-styled titlebar
- Distinct app identity β its own Wayland
app_id, so the dock / taskbar / alt-tab treat it as a separate application - Single instance β each app is a singleton: relaunching it (from the menu, dock, or CLI) focuses the existing window instead of opening a duplicate
- System tray β icon with a live unread badge, run-in-background, close-to-tray (hides/restores at the exact same position and size β no minimize animation, no flicker), and Quit to fully stop the app. The tray survives a Plasma restart, and closing never strands a window β if no tray is available the X closes the window normally
- Smart link routing β out-of-scope links open in your default browser, while the app's own domains and auth/SSO providers stay in-window
- Lightweight runtime β symlinks your system Firefox (a few hundred KB), so it tracks Firefox updates instead of bundling a second copy
- Desktop integration β generates
.desktoplaunchers and icons for you
- Firefox β₯ 151 (provides the Web Apps / Taskbar Tabs modules; used as the linked runtime)
- Rust / Cargo to build
- KDE Plasma β the tray hide/show and window control currently target KWin
(
qdbus6)
cd packages/arch
makepkg -sicargo build --release --bin ffwebapps --bin ffwebapps-tray # Put target/release/ffwebapps and ffwebapps-tray on your PATH, and copy # ./userchrome to /usr/share/ffwebapps/userchrome (or point FFPWA_SYSDATA at it).
One-time setup (link your system Firefox as the runtime):
ffwebapps runtime install --link
Install an app:
ffwebapps site install <MANIFEST_URL> --document-url <PAGE_URL> --name "App Name"
It then appears in your application menu as a chromeless window with a tray icon.
<MANIFEST_URL>is the site's web-app manifest (the<link rel="manifest" href="...">on the page);--document-urlis the page itself.
Daily use:
- Launch β from your app menu, or
ffwebapps site launch <ULID>. If the app is already running, this focuses the existing window (single instance) instead of opening a second one. - Close β tray β the window's X hides it to the tray; the app keeps running. If the tray isn't available, the X closes the window normally β it never gets stuck.
- Restore β single-click the tray icon (toggles hide/show)
- Quit β right-click the tray icon β Quit (fully stops the app)
- Unread β shown as a badge on the tray icon
- External links β open in your default browser automatically
ffwebapps runtime install [--link] | uninstall | patch ffwebapps site install <MANIFEST_URL> [--document-url --name --start-url --profile --launch-now --hardware-webrtc --scheduling <spec> ...] ffwebapps site launch <ULID> [--url <URL> | --protocol [<URL>]] ffwebapps site update <ULID> [--update-manifest --update-icons --hardware-webrtc <bool> --scheduling <spec>] ffwebapps site uninstall <ULID> ffwebapps profile list # lists profiles + their apps and ULIDs ffwebapps profile create | update <ULID> | remove <ULID>
Run ffwebapps <command> --help for the full flag list.
Per-app preferences live in the app's profile
(~/.local/share/ffwebapps/profiles/<profile>/):
user.jsffwebapps.externalLinks.enabledβ toggle external-link routingffwebapps.allowedDomainsβ comma-separated wildcard domains kept in-window
chrome/userChrome.cssβ titlebar colour and chrome tweaks (default#000)
For heavy apps like Teams / WhatsApp, two opt-in knobs are exposed (off by
default; set them per app at install/update time):
-
--hardware-webrtcβ force/maximise hardware video decoding for calls. On Linux, Firefox already GPU-decodes regular video and the WebRTC H.264/VP9 paths by default; this writesmedia.hardware-video-decoding.force-enabled(bypass Firefox's GPU blocklist) andmedia.navigator.mediadatadecoder_vp8_hardware_enabled(the HW VP8 path used by WhatsApp/Meet). Needs a working VA-API driver; can expose driver bugs, hence opt-in. Verify inabout:support(Media β decoder) andabout:webrtcduring a call (inbound video should use a hardware decoder). -
--scheduling <spec>β run the runtime under a scheduling policy to keep audio/video glitch-free under load. Specs:nice:-5(gentle, always works),rr:5/fifo:5(real-time; needrtprioprivileges β e.g. membership in a group withrtprioin/etc/security/limits.conf),batch,idle. RT policies gracefully fall back to normal scheduling if they can't be applied.
There is no hardware-audio knob β WebRTC audio (Opus) is CPU-cheap and has no GPU path. Echo-cancel / noise-suppression / auto-gain are on by default. The audio win under load is
--scheduling(RT), not a pref.
# e.g. Teams with forced HW video decode + real-time scheduling ffwebapps site update <ULID> --hardware-webrtc true --scheduling rr:5
ffwebapps drives Firefox's first-party Web Apps (Taskbar Tabs) feature:
- The CLI writes a per-app entry into the profile's
taskbartabs.jsonregistry plus a small runtime autoconfig (enables the feature and handles external links). - It launches the runtime with
firefox -taskbar-tab <id>, which opens a standalone minimal-UI window with a per-appapp_id. userChrome.cssstrips the remaining toolbar for a chromeless look.- A small
ffwebapps-trayhelper (a StatusNotifierItem) shows the icon/badge and hides/restores the window via KWin.
No browser-chrome monkeypatching β it builds on maintained Mozilla code.
- Tray hide/show and window control target KDE Plasma (KWin); on other desktops the icon appears but hide/restore won't work yet.
- No drop shadow on the chromeless window (a Wayland CSD limitation).
- CLI-only: you supply the manifest URL (there is no browser extension for auto-discovery).
A fork of PWAsForFirefox by
Filip Ε . Licensed under MPL-2.0 β see LICENSE.