Stream any GNU/Linux GUI application directly to a web browser with low(-enough) latency and automatic window resizing.
No VNC, no special client software, no websockify - just a simple HTTP server that makes desktop applications accessible through modern web protocols.
WebX11 creates virtual X11 displays and streams them to web browsers using WebTransport (with WebSocket fallback).
It's designed to be simple, fast, and easy to deploy, and only have dependencies you probably already have.
You're a sysadmin and need to open a GUI application on a remote server in a whim?
Need to expose a GUI app for a lab?
WebX11 has got you!
System Dependencies:
- Xvfb - Virtual framebuffer X server
- openssl (optional) - For generating TLS certificates (if you use WebTransport)
Optionally, create a settings.json file (see Configuration below)
Run the server:
Key Features:
- 🚀 Low latency streaming via WebTransport (HTTP/3) or WebSocket fallback
- 📐 Automatic window resizing based on browser viewport
- 🎯 Direct X11 integration - no VNC or intermediary protocols
- 🔌 Simple HTTP API for creating and managing displays
- ⚡ Minimal dependencies - just Xvfb and OpenSSL
- 🎮 Full input support - mouse, keyboard, scroll, dead keys
- 📊 Built-in FPS counter (press F3 to toggle)
Note : Regarding the latency. Realistically, you're not going to do some heavy gaming on this. But I'm currently streaming my terminal on my 2k screen and the 12 FPS I have are more than enough for this usage, especially with the low input latency! Also, if you're able to do some optimizations and gain a few FPS, you're very welcome to make a PR \o/!
- WebX11 spawns virtual X displays using Xvfb
- Captures window content and encodes it as WebP
- Streams frames via WebTransport streams (or WebSocket)
- Sends input events (mouse/keyboard) back to X11
- Automatically resizes the X display to match browser window
Create a settings.json file in the project root:
Note: Keep max_width and max_height values superior to the maximum size your windows will be resized to. High values do not actually have a significative impact on performance with the resize-x11 parameter.
resize_mode | string | "resize-x11" enables automatic X display resizing, "none" disables it, "stretch" stretches the image without actual resizing |
transport | string | "webtransport" (recommended) or "websocket" |
image_quality | number | WebP quality (1-100), affects bandwidth and visual quality |
dpi | number | Display DPI setting |
max_width | number | Maximum display width in pixels |
max_height | number | Maximum display height in pixels |
max_fps | number | Maximum frames per second (1-60) |
can_start_executables | boolean | Allow starting executables via API (security consideration) |
The server will start on:
- HTTP API: http://localhost:8080
- WebSocket: ws://localhost:8081
- WebTransport: https://localhost:4433
Option 1: Using the HTTP API
Response:
Option 2: Starting with an Application (if can_start_executables: true)
Open your browser and navigate to:
or
To access a specific display using its id.
For WebTransport (recommended):
- Chrome/Edge with command-line flags (see below)
- The browser must accept the self-signed certificate
Starting Chrome with WebTransport support:
The certificate fingerprint is printed when the server starts.
For WebSocket fallback:
- Any modern browser (Firefox, Safari, Chrome, Edge)
- No special configuration needed
List all active displays
Response:
Create a new display
Note: Make sure the display you create is always bigger than the max resize area possible. The X server will crash on resize otherwise.
Starting with a very large display size is not an issue, the first automatic resize will resize it into a smaller display.
Request:
Response:
Close a display
Response:
Start an executable on a display (requires can_start_executables: true)
Request:
Response:
Manually resize a display
Response:
Access the web interface for a display
Returns an HTML page with the interactive display viewer.
If the server is started with no executable as a parameter, a display needs to be created via the HTTP API.
Built-in shortcuts:
- F3 - Toggle FPS counter
WebTransport Mode:
- Each frame is sent on a separate unidirectional stream
- Control messages (input) use datagrams
- 10-30 FPS depending on configuration
- Lower latency than WebSocket
WebSocket Mode:
- Frames sent as binary blobs
- 8~25 FPS depending on configuration
- Control messages as JSON
- Works everywhere, no special setup needed
- Lower image_quality (60-80) for better performance on slow networks
- Reduce max_fps (15-20) if CPU usage is high
- Use WebTransport when possible for lowest latency
- Match max_width/max_height to your typical use case
- Enable resize mode to adapt to different screen sizes
This project is a small project, the code has not been designed with the utmost security in mind and as of today does not include authentication. It should NOT be exposed to the internet, especially on sensitive infrastructures! The WebSockets and HTTP APIs use no encryption, keep that in mind.
- TLS Certificates: WebTransport uses self-signed certificates by default
- can_start_executables: Set to false in production to prevent arbitrary code execution
- No authentication: Consider adding authentication for production deployments
- No encryption : Bring your own, by putting a HTTPS/WSS reverse proxy in front of WebX11
- Network exposure: By default, only binds to localhost
- Ensure Chrome is started with the correct flags
- Check that port 4433 is not blocked
- Verify the certificate fingerprint matches the generated certificates
- Check that Xvfb is installed and working
- Verify that your executable is correctly running on the display
- Check console logs for errors
- Try reducing image quality
- Click on the display area to ensure focus
- Check browser console for errors
- International keyboards should work automatically but some layouts are still a WIP
- Reduce max_fps in settings
- Lower image_quality
- Check if multiple displays are running
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes
- Submit a pull request
- Built with aioquic for WebTransport support
- Uses python-xlib for X11 integration
- Inspired by the need for simple, clientless desktop streaming
For issues, questions, or feature requests, please open an issue on GitHub.