Browser-based VNC client powered by noVNC and Fastify.
A production-ready, self-hostable VNC client for the browser. Connect to any VNC server directly from a clean, minimal dark web interface — no plugins, no Java, no desktop app required.
- Features
- How it works
- Requirements
- Quick start
- Setting up a VNC server
- WebSocket bridge
- Configuration
- URL parameters
- API
- Production deployment
- Docker
- Development scripts
- Project structure
- Windows installer
- Roadmap
- Full noVNC integration with WebSocket VNC proxying
- Fastify backend — structured logging, plugin-based, strict TypeScript
- Dark minimal UI — CSS design system, responsive, no frontend framework
- Scale modes: fit-to-window, actual pixels (1:1, scrollable), remote resize, and custom zoom
- View-only toggle, screenshot capture, power actions (shutdown/reboot/reset)
- Open multiple simultaneous connections in separate tabs
- All noVNC options: encryption, shared sessions, view-only, clipboard, quality, reconnect
- Secure defaults: Helmet headers, rate limiting, CORS, Zod-validated config
- Docker ready with built-in websockify bridge
- Vitest test suite with coverage
Browser <──WebSocket──> WebVNC (Fastify) <──TCP──> VNC Server
(noVNC) built-in proxy
(websockify)
The browser runs noVNC, which speaks the RFB protocol over a WebSocket connection. WebVNC's built-in websockify bridge proxies that WebSocket to a raw TCP VNC port on the same or a remote machine. No standalone websockify installation is required unless you prefer to manage the bridge separately.
| Dependency | Minimum version | Notes |
|---|---|---|
| Node.js | 20.x | Required |
| npm | 10.x | Bundled with Node.js |
| A VNC server | — | See Setting up a VNC server |
| Python 3 + websockify | — | Only if not using the built-in bridge |
# 1. Clone the repository
git clone https://github.com/elias4044/webvnc.git
cd webvnc
# 2. Install dependencies
npm install
# 3. Copy and edit the environment config
cp .env.example .env
# 4. Start in development mode (Fastify + Vite HMR in parallel)
npm run devOpen http://localhost:3020 and fill in the connection form.
For a fast first test without any VNC server, set the host/port to an existing RealVNC, TigerVNC, or UltraVNC machine on your network. See the next section for how to set one up from scratch.
WebVNC is a client — it connects to an existing VNC server. Below are setup guides for the most common options.
UltraVNC is a high-performance open-source VNC server for Windows. It supports hardware-accelerated screen capture via the Mirror Driver and has good compatibility with noVNC.
Installation
-
Download the installer from https://uvnc.com/downloads/ultravnc.html. Choose the latest stable release for your architecture (32-bit or 64-bit).
-
Run the installer. On the Select Components screen, enable:
- UltraVNC Server
- UltraVNC Viewer (optional, useful for local testing)
- Mirror Driver (recommended — significantly improves performance on older hardware)
-
During installation you will be prompted to set a VNC password. This is the password WebVNC will prompt for when connecting. Choose a strong password.
Configuration
After installation, open UltraVNC Server from the system tray icon and click Admin Properties:
| Setting | Recommended value | Reason |
|---|---|---|
| VNC Password | (your chosen password) | Mandatory for secure access |
| MS-Logon II | Off (unless you need Windows auth) | Simplifies setup |
| Query on connect | Off (or On if you want pop-up approval) | Personal preference |
| Allow Loopback connections | On | Required if testing locally |
| Accept Incoming Connections | On | Must be enabled |
| Port | 5900 | Default; change if needed |
Click Apply then restart the UltraVNC service via the tray icon (Restart Service).
Firewall
Open Windows Firewall and allow inbound TCP traffic on port 5900 (or whichever port you configured). If you are only accessing WebVNC from the same machine, this is not necessary.
Mirror Driver (optional but recommended)
If you installed the Mirror Driver, enable it in Admin Properties under the Capture tab. It reduces CPU usage and improves frame rates dramatically.
# Ubuntu / Debian
sudo apt update && sudo apt install tigervnc-standalone-server
# Set a VNC password
vncpasswd
# Start a virtual display on screen :1 (port 5901)
vncserver :1 -geometry 1920x1080 -depth 24To run a full desktop session, create ~/.vnc/xstartup:
#!/bin/bash
exec /usr/bin/startxfce4 # or gnome-session, openbox, etc.chmod +x ~/.vnc/xstartup
vncserver :1 -geometry 1920x1080 -depth 24TigerVNC listens on port 5900 + display_number, so :1 = port 5901.
Download from https://www.realvnc.com/en/connect/download/vnc/. RealVNC Server supports WebSocket natively on Enterprise editions. On free/Home editions, use the built-in websockify bridge (see below).
VNC uses a raw TCP socket. Browsers can only use WebSockets. A bridge is required.
WebVNC ships with a built-in WebSocket-to-TCP proxy (enabled by default via
WEBSOCKIFY_ENABLED=true in .env). It listens on WEBSOCKIFY_PORT (default 6080)
and forwards connections to VNC_DEFAULT_HOST:VNC_DEFAULT_PORT.
With the built-in bridge active you point noVNC at the WebVNC server:
VNC Server port: 5900 (raw TCP, UltraVNC / TigerVNC / etc.)
WebVNC port: 6080 (WebSocket, what the browser connects to)
Set VNC_DEFAULT_PORT=6080 in .env (or enter 6080 in the UI).
No additional software is required.
Use this if you prefer to manage the bridge as a separate process, or if you are running the VNC server on a different machine.
Installation
# Python 3 required
pip install websockifyUsage
# Forward WebSocket port 6080 to VNC TCP port 5900 on the same machine
websockify 6080 localhost:5900
# Forward to a VNC server on a different host
websockify 6080 192.168.1.50:5900
# With SSL termination
websockify --cert /path/to/cert.pem --key /path/to/key.pem 6081 localhost:5900When using standalone websockify, set WEBSOCKIFY_ENABLED=false in .env to disable
the built-in proxy. Set VNC_DEFAULT_PORT to the websockify listen port.
TigerVNC 1.11+, RealVNC Enterprise, and some other servers support WebSocket directly.
In this case no bridge is needed at all. Point WebVNC directly at the VNC server's
WebSocket port and set WEBSOCKIFY_ENABLED=false.
All configuration is via environment variables. Copy .env.example to .env and edit:
cp .env.example .env| Variable | Default | Description |
|---|---|---|
NODE_ENV |
development |
development / production / test |
HOST |
0.0.0.0 |
Bind address for the WebVNC HTTP server |
PORT |
3020 |
HTTP port for the WebVNC server |
LOG_LEVEL |
info |
Pino log level (trace debug info warn error) |
CORS_ORIGIN |
* |
Allowed CORS origin. Set to your domain in production |
RATE_LIMIT_MAX |
100 |
Maximum requests per window per IP |
RATE_LIMIT_WINDOW_MS |
60000 |
Rate limit window in milliseconds |
VNC_DEFAULT_HOST |
localhost |
Default VNC host pre-filled in the UI |
VNC_DEFAULT_PORT |
6080 |
Default VNC port pre-filled in the UI |
VNC_DEFAULT_PATH |
/ |
Default WebSocket path |
VNC_DEFAULT_ENCRYPT |
false |
Use wss:// by default |
WEBSOCKIFY_ENABLED |
true |
Enable the built-in WebSocket-to-TCP bridge |
WEBSOCKIFY_PORT |
6080 |
Port the built-in bridge listens on |
WEBSOCKIFY_HOST |
0.0.0.0 |
Bind address for the built-in bridge |
TRUST_PROXY |
false |
Set true when running behind nginx / traefik / Caddy |
The /vnc.html viewer page accepts all connection settings as URL query parameters.
This is useful for bookmarks, deep links, kiosk setups, or embedding.
http://localhost:3020/vnc.html?host=myserver&port=6080&encrypt=false&autoConnect=true
| Parameter | Type | Default | Description |
|---|---|---|---|
host |
string | localhost |
VNC server hostname or IP |
port |
number | 6080 |
WebSocket port |
path |
string | / |
WebSocket path |
password |
string | — | Pre-fill password (not stored or logged) |
encrypt |
boolean | false |
Use wss:// instead of ws:// |
shared |
boolean | true |
Allow multiple simultaneous VNC connections |
viewOnly |
boolean | false |
Disable keyboard/mouse input |
autoConnect |
boolean | false |
Connect immediately on page load |
reconnect |
boolean | false |
Auto-reconnect on unexpected disconnect |
reconnectDelay |
number | 3020 |
Delay before reconnect attempt (ms) |
repeaterId |
string | — | UltraVNC repeater ID |
qualityLevel |
number | 6 |
JPEG quality 0–9 (0 = lossless) |
compressionLevel |
number | 2 |
Zlib compression 0–9 |
showDotCursor |
boolean | false |
Show a dot cursor when the remote cursor is hidden |
clipboardUp |
boolean | true |
Send local clipboard to remote |
clipboardDown |
boolean | true |
Receive remote clipboard locally |
scaleMode |
string | fit |
fit / real / remote / zoom |
zoomLevel |
number | 1.0 |
Zoom multiplier when scaleMode=zoom |
| Method | Path | Description |
|---|---|---|
GET |
/api/health |
Health check — returns server status |
GET |
/api/vnc/defaults |
Returns server-configured VNC defaults |
POST |
/api/vnc/validate |
Validates connection parameter object |
# Build client and server
npm run build
# Start the production server
npm startThe server listens on HOST:PORT (default 0.0.0.0:3020).
Recommended nginx reverse proxy config:
server {
listen 443 ssl;
server_name vnc.example.com;
ssl_certificate /etc/letsencrypt/live/vnc.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/vnc.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3020;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600s;
}
}Set TRUST_PROXY=true and CORS_ORIGIN=https://vnc.example.com in .env.
# Build and start with docker compose
docker compose up -d
# View logs
docker compose logs -f
# Stop
docker compose downThe compose file exposes port 3020 (HTTP) and 6080 (built-in websockify bridge).
Set VNC target host/port via environment variables in .env or directly in
docker-compose.yml.
To connect to a VNC server running on the Docker host machine, use the host's LAN IP
rather than localhost inside the container (172.17.0.1 on default Docker networks,
or use host.docker.internal on Docker Desktop).
| Command | Description |
|---|---|
npm run dev |
Start all dev servers (Fastify + Vite HMR) |
npm run build |
Production build (client + server) |
npm start |
Start production server |
npm test |
Run test suite |
npm run test:coverage |
Run tests with coverage report |
npm run lint |
Lint TypeScript |
npm run lint:fix |
Auto-fix lint issues |
npm run format |
Format source files with Prettier |
npm run typecheck |
Type-check without emitting |
npm run clean |
Delete dist/ |
src/
server/
config/ Zod-validated environment config
plugins/ Fastify plugins (CORS, Helmet, rate-limit, static)
routes/
api/ /api/health, /api/vnc/* route handlers
services/ Websockify bridge and other server services
app.ts Fastify app factory
index.ts Server entry point
types.ts Shared server types
client/
components/ Dashboard UI components (ConnectionForm, AdvancedSettings, etc.)
vnc/ noVNC integration (VncClient, VncApp)
styles/ CSS design system (global, components, dashboard, vnc)
utils/ params (URL/storage serialisation), events (emitter, logger)
main.ts Dashboard entry point
types.ts Shared client types
tests/
server/ Fastify route integration tests
client/ Client utility unit tests
installer/
webvnc-setup.ps1 Windows interactive installer script
build-exe.ps1 Compiles the installer script to a standalone .exe
An interactive PowerShell installer is included under installer/. It handles the
full setup on Windows without any prior knowledge of Node.js or npm.
What the installer does:
- Checks for Node.js 20+ and installs it via
wingetif missing - Checks for Python 3 and installs it via
wingetif missing (needed for standalone websockify) - Installs
websockifyviapip(optional, only if you decline the built-in bridge) - Runs
npm installandnpm run build - Creates a
.envfrom.env.examplewith values you provide interactively - Optionally opens the UltraVNC download page in your browser
- Creates a
Start WebVNC.batlauncher on your Desktop
Running the PowerShell script directly:
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
.\installer\webvnc-setup.ps1Building a standalone .exe:
The build-exe.ps1 script compiles the installer into a self-contained .exe using
ps2exe from the PowerShell Gallery.
.\installer\build-exe.ps1
# Output: installer\WebVNC-Setup.exeRun WebVNC-Setup.exe on any Windows 10/11 machine — no PowerShell knowledge required.
- Connection profiles (save and switch named sessions)
- Built-in access control / authentication layer
- Audit log for connections
- Multi-session management dashboard
- Theme presets
- WebRTC tunnel option
See CONTRIBUTING.md.
MIT © 2026 elias4044