Lumen is a Wayland compositor that streams your Linux desktop to a web browser over WebRTC — no client software required.
Warning
This project is currently experimental and not yet stable. Use at your own risk! Please report any problems you encounter.
Lumen exists to replace legacy Linux remote desktop solutions (VNC, RDP, NX, and similar) for the Wayland era. The goal is to provide a high-performance, low-latency desktop streaming experience accessible from any modern browser, with no plugins or native clients needed on the viewer side.
Supports desktop and mobile browsers, including ChromeOS, Android and iOS. Connect from wherever you are.
There are countless other solutions to this problem, so why another?
- Many applications don't work (well) with Wayland making them difficult to configure and use on bleeding edge Linux distributions.
- Most require client side software making it difficult or inconsistent to use if you use multiple types of devices or operating systems.
- Hardware acceleration support is inconsistent, they either rely entirely on software rendering or specific encoding hardware making them difficult to use or slow for realtime applications.
- Wayland-Native: Built on Smithay — modern, secure window management and frame capture.
- WebRTC Streaming: Low-latency H.264 video and Opus audio delivered directly to any modern browser via the str0m WebRTC stack.
- Hardware Acceleration: VA-API (Intel/AMD) zero-copy H.264 encoding with automatic x264 software fallback (experimental NVENC support exists).
- System Audio: PipeWire capture encoded to Opus.
- Interactive Input: Keyboard, mouse, and clipboard forwarded from the browser back into the Wayland session.
- Embedded TURN Server: Built-in TURN relay so the stream works across NAT without an external relay.
- Authentication: Optional auth modes — HTTP Basic (PAM), bearer token, or OAuth2/OIDC.
- TLS: Native HTTPS/WSS support via PEM certificate and key.
- Single Binary: Built-in web server and signaling — just run
lumenand open a browser.
Lumen is organized as a Rust workspace with several specialized crates:
lumen-compositor: The core Wayland compositor logic using Smithay. Handles window management, rendering, and frame capture.lumen-webrtc: Manages WebRTC sessions, ICE negotiation (via str0m), and RTP packetization for H.264 and Opus.lumen-encode: Video encoding abstraction layer. Supports VA-API and software (x264) backends.lumen-audio: Creates a virtual PipeWire audio sink; applications route audio to it, and the crate captures, encodes with Opus, and delivers packets for WebRTC.lumen-web: An Axum-based web server that serves the frontend client and handles WebSocket signaling.lumen-turn: Embedded TURN/STUN relay server so WebRTC peers behind NAT can exchange media without an external relay.lumen-gamepad: Virtual gamepad devices viauinput— browser-connected gamepads appear as standard/dev/inputdevices to applications.web/: The frontend browser client built with vanilla JavaScript and Web APIs.
Download the latest package for your distribution from the Lumen releases page, then install it:
# Ubuntu / Debian
sudo apt install ./lumen_*_amd64.deb
# Fedora / RHEL
sudo dnf install ./lumen-*.rpmAfter installation, create a config file and start the service:
sudo cp /etc/lumen/example.env /etc/lumen/<username>.env
sudo nano /etc/lumen/<username>.env # set LUMEN_LAUNCH, auth, etc.
sudo systemctl start lumen@<username>See pkgs/README.md for the full build, configuration, and service management guide.
Pre-built images are available on the GitHub Container Registry:
# labwc (lightweight Wayland compositor) — recommended
podman run --rm -it --device /dev/dri --network host ghcr.io/swedishborgie/lumen:latest-labwc
# KDE Plasma 6
podman run --rm -it --device /dev/dri --network host ghcr.io/swedishborgie/lumen:latest-kdeSee docker/README.md for full GPU passthrough, gamepad, and networking options.
- Rust (latest stable)
- PipeWire
- VA-API compatible drivers (optional, for hardware acceleration)
- Native library development headers:
libx264,libva,libopus,libpipewire,libwayland,libxkbcommon,libpixman,libinput,libpam,libssl, and FFmpeg
See docs/for-developers.md for the full per-distro package list.
cargo build --releaseYou can run Lumen with default settings:
cargo run --releaseThen, open your browser and navigate to http://localhost:8080.
All options can be set via command-line flags or environment variables.
| Flag | Env | Default | Description |
|---|---|---|---|
--bind-addr |
LUMEN_BIND |
0.0.0.0:8080 |
Address to bind the web server to |
--hostname |
LUMEN_HOSTNAME |
(OS hostname) | Hostname shown in the browser tab title and PWA app name |
--width |
LUMEN_WIDTH |
1920 |
Output width in pixels |
--height |
LUMEN_HEIGHT |
1080 |
Output height in pixels |
--fps |
LUMEN_FPS |
30.0 |
Target frames per second |
--video-bitrate-kbps |
LUMEN_VIDEO_BITRATE_KBPS |
4000 |
Target video bitrate (kbps) |
--max-bitrate-kbps |
LUMEN_MAX_BITRATE_KBPS |
2× video-bitrate |
Peak VBR bitrate cap (kbps) |
--audio-bitrate-bps |
LUMEN_AUDIO_BITRATE_BPS |
128000 |
Opus audio bitrate (bps) |
--dri-node |
LUMEN_DRI_NODE |
(auto-detected) | DRI render node for VA-API (e.g. /dev/dri/renderD128) |
--cuda-device |
LUMEN_CUDA_DEVICE |
0 |
CUDA device index for NVENC hardware encoding (nvenc feature only); set to empty string to disable NVENC |
--inner-display |
LUMEN_INNER_DISPLAY |
auto |
Wayland socket of a nested compositor to bridge clipboard from; set to "" to disable |
--ice-servers |
LUMEN_ICE_SERVERS |
stun:stun.l.google.com:19302 |
Comma-separated ICE/STUN server URLs |
--launch |
LUMEN_LAUNCH |
Shell command to launch as a Wayland client once the compositor is ready (e.g. labwc, sway) |
Lumen includes an embedded TURN server to relay WebRTC traffic across NAT. It is enabled by default on port 3478.
| Flag | Env | Default | Description |
|---|---|---|---|
--turn-port |
LUMEN_TURN_PORT |
3478 |
UDP port for the TURN server (set to 0 to disable) |
--turn-external-ip |
LUMEN_TURN_EXTERNAL_IP |
(auto-detected) | Public IP to advertise as the TURN relay address |
--turn-username |
LUMEN_TURN_USERNAME |
(auto-generated) | TURN username |
--turn-password |
LUMEN_TURN_PASSWORD |
(auto-generated) | TURN password |
--turn-min-port |
LUMEN_TURN_MIN_PORT |
50000 |
Low end of UDP relay port range |
--turn-max-port |
LUMEN_TURN_MAX_PORT |
50010 |
High end of UDP relay port range |
| Flag | Env | Default | Description |
|---|---|---|---|
--auth |
LUMEN_AUTH |
basic |
Auth mode: none, basic (PAM), bearer, or oauth2 (OIDC) |
--auth-bearer-token |
LUMEN_AUTH_BEARER_TOKEN |
[bearer] Preshared token; clients must send Authorization: Bearer <token> |
|
--auth-oauth2-issuer-url |
LUMEN_AUTH_OAUTH2_ISSUER_URL |
[oauth2] OIDC issuer URL (discovery fetched from /.well-known/openid-configuration) |
|
--auth-oauth2-client-id |
LUMEN_AUTH_OAUTH2_CLIENT_ID |
[oauth2] OAuth2 client ID |
|
--auth-oauth2-client-secret |
LUMEN_AUTH_OAUTH2_CLIENT_SECRET |
[oauth2] OAuth2 client secret |
|
--auth-oauth2-redirect-uri |
LUMEN_AUTH_OAUTH2_REDIRECT_URI |
[oauth2] Full redirect URI (e.g. http://localhost:8080/auth/callback) |
|
--auth-oauth2-subject |
LUMEN_AUTH_OAUTH2_SUBJECT |
[oauth2] Expected sub claim; access denied if it doesn't match |
| Flag | Env | Description |
|---|---|---|
--tls-cert |
LUMEN_TLS_CERT |
Path to a PEM TLS certificate chain; enables HTTPS when paired with --tls-key |
--tls-key |
LUMEN_TLS_KEY |
Path to a PEM TLS private key; must be provided together with --tls-cert |
- Compositor: Renders the desktop/applications. Frames are captured and sent to the Encoder.
- Audio: Captures audio samples from PipeWire and encodes them into Opus packets.
- Encoder: Takes raw frames and produces H.264 bitstream (using VA-API if available).
- Signaling: The browser connects to the Web Server via WebSocket to exchange SDP offers/answers and ICE candidates.
- WebRTC: Once the connection is established, H.264 and Opus packets are sent over SRTP. Input events are sent back from the browser via WebRTC Data Channels.
- Input: The Compositor receives input events and injects them into the virtual keyboard/pointer devices.
The architecture for this application was heavily inspired by the excellent Selkies and Pixelflux projects.
