Stream your entire PC to the Tesla in-car browser using Moonlight + Sunshine over WebRTC. Play games, watch YouTube/Netflix/Twitch, browse the web, or run any desktop application — right on the Tesla touchscreen. Works while parked, charging and in D mode.
This is a Tesla-optimized fork of moonlight-web-stream — a browser-based Moonlight client. It includes dozens of Tesla-specific fixes for audio, video, input/gamepad handling, and WebSocket/WebRTC reliability that are required because the Tesla Chromium browser behaves differently from standard desktop browsers.
It hosts a lightweight Web Server which forwards Sunshine traffic to the browser using the WebRTC API. No additional hardware or apps required — just your PC running Sunshine, port forwarding, and a domain name.
https://youtu.be/whdvHChCQbg?si=WLcgPDclkdr8n41i
- Images
- Limitations
- Installation
- Setup
- URL Parameters
- Config
- Contributors
- Building
- Why this fork exists
- Features that only work in a Secure Context -> How to configure a Secure Context / https
- Keyboard Lock (allows to capture almost all keys also OS Keys): Experimental Keyboard Lock API
- Controllers (USB/Bluetooth gamepads): work over HTTP via the Gamepad API — no HTTPS required
- Tesla browser: keyboard and mouse do not work natively. Use a second device (e.g. an Android phone) as the input device via Attach Input Only
-
Install Sunshine
-
Download the compressed archive for your platform and extract it
The included setup.ps1 wizard handles everything interactively:
- Detects your network configuration
- Sets a password for the web interface
- Configures port forwarding via UPnP
- Adds Windows Firewall rules
- Optionally obtains a free Let's Encrypt HTTPS certificate
- Creates a scheduled task for automatic certificate renewal
- Starts the server
Run as Administrator (required for firewall rules and UPnP):
# Right-click PowerShell → "Run as administrator", then:
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
.\setup.ps1Or simply right-click setup.ps1 → Run with PowerShell (it will self-elevate).
Follow the prompts. The wizard will guide you through each step and explain what it is doing.
HTTPS / Tesla access: Choose option 1 (Let's Encrypt) in the certificate step. You need a domain name first — see Getting a free domain name.
-
Run the
web-serverexecutable -
Change your access credentials in the generated
server/config.json(requires restart) -
Go to
http://localhost:43780and view the web interface
Add your PC:
-
Add a new PC (
) with the address
localhostand leave the port empty (if you're using the default port) -
Pair your PC by clicking on the host (
) and entering the code in Sunshine
-
Launch an app
The Tesla browser enforces strict security policies. Accessing Moonlight Web via a local IP address (e.g. 192.168.1.x) will typically result in an Access Denied error. You must access it via a proper domain name — the Tesla browser blocks all requests to raw IP addresses and local network ranges.
Requirements for Tesla:
- A domain name pointing to your public IP (see Getting a free domain name below)
- Port forwarding configured on your router
- (Recommended) An HTTPS certificate for the domain — see Configuring https
Keyboard and mouse input do not work natively in the Tesla browser. To type or use a pointer, attach a second device as a dedicated input client:
- Start the stream on the Tesla as usual.
- On a second device (an Android phone works well) open the same Moonlight Web server and log in.
- In the games list, click (or long-press) the currently running game.
- Choose "Attach Input Only" from the context menu.
This opens a second, input-only connection (input.html?hostId=<id>) that carries keyboard, mouse,
touch and controller input to the host but no video or audio. The Tesla keeps showing the picture;
the phone becomes the keyboard and trackpad.
The option only appears while a session is actually running on that host. Several input devices can be attached at once, the stream page shows an indicator when any are connected, and detaching one does not disturb the video stream on the Tesla.
You need a domain name that resolves to your home public IP. Here are free options:
| Service | Domain Format | Notes |
|---|---|---|
| ASUS Router DDNS | yourname.asuscomm.com |
Built into ASUS routers, auto-updates IP |
| DuckDNS | yourname.duckdns.org |
Free, simple, supports auto-update scripts |
| No-IP | yourname.ddns.net |
Free tier (confirm monthly) |
| Cloudflare | Your own domain | Free DNS management, buy domain elsewhere (~$10/yr) |
| Afraid.org FreeDNS | Various subdomains | Free, many domain options |
Tip: Most routers have a built-in DDNS client. Check your router admin panel under WAN → DDNS. This keeps the DNS record updated automatically when your public IP changes.
Check your current public IP at whatismyip.com. Your DNS record must point to this IP.
Required ports to open (in your router/firewall):
| Port | Protocol | Purpose |
|---|---|---|
| 43780 | TCP | Web interface (or whichever port you configured) |
| 40000–40100 | UDP | WebRTC media stream |
Windows Firewall: On some systems you may need to add inbound rules for UDP 40000–40100. Try temporarily disabling Windows Firewall to test if it's blocking traffic. If that helps, add the rules and re-enable it. Also make sure your network profile is set to Private.
Steps to get it working from the Tesla browser:
-
Set up your domain — follow Getting a free domain name above if you don't already have one.
-
Set your public IP in the config (
server/config.json):{ "webrtc_nat_1to1": { "ice_candidate_type": "srflx", "ips": [ "12.34.56.78" ] } }Replace
12.34.56.78with your actual public IP (only digits and dots, no<or>). -
Forward ports in your router:
- TCP 43780 → your PC's local IP (e.g.
192.168.1.50) - UDP 40000–40100 → your PC's local IP
Tip: The
setup.ps1wizard can do this automatically via UPnP. - TCP 43780 → your PC's local IP (e.g.
-
Use the Cloudflare / Google STUN servers and
udp4only (already in the default config):{ "webrtc_ice_servers": [ { "urls": [ "stun:stun.cloudflare.com:3478", "stun:stun.l.google.com:19302", "stun:stun1.l.google.com:3478", "stun:stun2.l.google.com:19302", "stun:stun3.l.google.com:3478", "stun:stun4.l.google.com:19302" ] } ], "webrtc_network_types": ["udp4"] } -
Access the web interface from your Tesla browser via
https://yourdomain.com:43780— or on port 443 if you forwarded that.
Note: Some routers let you export a signed certificate directly. See Configuring https.
Stream connects on PC/phone (local Wi-Fi) but not over cellular or in the Tesla browser:
- The most common cause is a missing or wrong public IP in
webrtc_nat_1to1. Double-check it matches whatismyip.com. - Make sure UDP 40000–40100 is forwarded in your router to your PC.
- Try temporarily disabling Windows Firewall to isolate whether it is blocking UDP.
Tesla browser shows "Access Denied":
- You are likely trying to access via a local IP address. The Tesla browser blocks local addresses. Use a domain name with a proper DNS record pointing to your public IP.
- Make sure your Tesla is on the same Wi-Fi or using cellular, and that the domain resolves to your router's public IP.
Audio is not playing after stream starts:
- This is a browser autoplay restriction. Tap or click anywhere inside the stream after it starts — this triggers the AudioContext to resume.
- Make sure audio is actually playing on the host PC (not muted).
- Turn up the volume in the Tesla.
- If using Apollo instead of Sunshine, try switching to Sunshine. Apollo has been observed to have performance issues with this client.
- In Sunshine audio settings: leave Audio sink and Virtual sink empty (auto), and ensure both audio checkboxes are checked.
- When streaming is active, check the Windows Volume Mixer — you should see "Steam Streaming Speakers" with an active level meter (audio is captured but not played locally).
The .exe won't start after editing config:
- The config is JSON — check for missing or extra commas, especially after the last item in a block.
- Make sure
webrtc_nat_1to1.ipscontains plain IP addresses like"12.34.56.78"with no<or>brackets.
- Set the bind address to your network interface and forward the web server port
{
"bind_address": "192.168.1.100:43780"
}When in a local network the WebRTC Peers will negotatiate without any problems. When you want to play to over the Internet the STUN servers included by default will try to negotiate the peers directly. This works for most of the networks, but if your network is very restrictive it might not work. If this is the case try to configure one or both of these options:
- The most reliable and recommended way is to use a turn server
- Forward the ports directly (this might not work if the firewall blocks udp)
-
Host and configure a turn server like coturn or use other services to host one for you.
-
Add your turn server to your WebRTC Ice Server list
{
"webrtc_ice_servers": [
{
"urls": [
"stun:l.google.com:19302",
"stun:stun.l.google.com:19302",
"stun:stun1.l.google.com:19302",
"stun:stun2.l.google.com:19302",
"stun:stun3.l.google.com:19302",
"stun:stun4.l.google.com:19302",
]
},
{
"urls": [
"turn:yourip.com:3478?transport=udp",
"turn:yourip.com:3478?transport=tcp",
"turn:yourip.com:5349?transport=tcp"
],
"username": "your username",
"credential": "your credential"
}
]
}Some (business) firewalls might be very strict and only allow tcp on port 443 for turn connections if that's the case also bind the turn server on port 443 and add "turn:yourip.com:443?transport=tcp" to the url's list.
- Set the port range used by the WebRTC Peer to a fixed range in the config
{
"webrtc_port_range": {
"min": 40000,
"max": 40100
}
}-
Forward the port range specified in the previous step as
udp. If you're using Windows Defender make sure to allow NAT Traversal. Important: If your firewall blocks udp connections this won't work and you need to host a turn server -
Configure WebRTC Nat 1 To 1 to advertise your public ip (Optional: WebRTC stun servers can usually automatically detect them):
{
"webrtc_nat_1to1": {
"ice_candidate_type": "host",
"ips": [
"74.125.224.72"
]
}
}It might be helpful to look what kind of nat your pc is behind:
You can configure HTTPS directly with the Moonlight Web Server.
The included setup.ps1 wizard handles this automatically on Windows. Run it and choose option 1 in the certificate step.
For manual use, the acme-certificate.ps1 script automates obtaining a free, trusted SSL certificate from Let's Encrypt.
Prerequisites:
- A domain name pointing to your public IP (e.g. DDNS like
*.asuscomm.com— see Getting a free domain) - Port 80 forwarded on your router to the server's local IP (the setup wizard can do this via UPnP)
Windows (standalone):
.\acme-certificate.ps1The script will prompt for your domain and server URL, then automatically:
- Generate an account key (reused on subsequent runs)
- Request a certificate from Let's Encrypt
- Set up the HTTP-01 challenge via the server's API
- Wait for validation
- Save
server/cert.pemandserver/key.pemand updateserver/config.json
Tip: Use
-Stagingto test with Let's Encrypt's staging environment first to avoid rate limits.
The setup.ps1 wizard creates this scheduled task automatically. To set it up manually:
- Open Task Scheduler → Create Task
- General tab: Name it
Moonlight Web Certificate Renewal, check "Run whether user is logged on or not" - Triggers tab: New → On a schedule, repeat every 60 days
- Actions tab: New →
- Program:
powershell.exe - Arguments:
-ExecutionPolicy Bypass -File "C:\path\to\acme-certificate.ps1" -Domain "yourdomain.com" -ServerUrl "http://192.168.1.100:43780" - Start in:
C:\path\to\moonlight-web\
- Program:
- Click OK and enter your Windows password
For local/development use where browser trust warnings are acceptable:
- Generate a self-signed certificate with the included Python script:
pip install pyOpenSSL
python ./moonlight-web/web-server/generate_certificate.py-
Copy the files
server/key.pemandserver/cert.peminto yourserverdirectory. -
Modify the config to enable https:
{
"certificate": {
"private_key_pem": "./server/key.pem",
"certificate_pem": "./server/cert.pem"
}
}Note: Self-signed certificates will show browser warnings and may not work in the Tesla browser.
It's possible to proxy the Moonlight Website using Apache 2.
Note: When you want to use https, the Moonlight Website should use http so that Apache 2 will handle all the https encryption.
- Enable the modules
mod_proxy,mod_proxy_wstunnel
sudo a2enmod mod_proxy mod_proxy_wstunnel- Create a new file under
/etc/apache2/conf-available/moonlight-web.confwith the content:
# Example subpath "/moonlight" -> To connect you'd go to "http://yourip.com/moonlight/"
Define MOONLIGHT_SUBPATH /moonlight
# The address and port of your Moonlight Web server
Define MOONLIGHT_STREAMER YOUR_LOCAL_IP:YOUR_PORT
ProxyPreserveHost on
# Important: This WebSocket will help negotiate the WebRTC Peers
<Location ${MOONLIGHT_SUBPATH}/api/host/stream>
ProxyPass ws://${MOONLIGHT_STREAMER}/api/host/stream
ProxyPassReverse ws://${MOONLIGHT_STREAMER}/api/host/stream
</Location>
ProxyPass ${MOONLIGHT_SUBPATH}/ http://${MOONLIGHT_STREAMER}/
ProxyPassReverse ${MOONLIGHT_SUBPATH}/ http://${MOONLIGHT_STREAMER}/
- Enable the created config file
sudo a2enconf moonlight-web- Change config to include the prefixed path
{
"web_path_prefix": "/moonlight"
}- Use https with a certificate (Optional)
The stream page accepts settings as query-string parameters, so a bookmark or launcher can pin a configuration without changing your saved settings. Overrides apply to that launch only and are never written back to the stored per-host settings.
stream.html?hostId=1&appId=2&bitrate=8000&fps=60&videoSize=1080p
| Parameter | Values | Description |
|---|---|---|
hostId |
number | Required. Which host to stream from. |
appId |
number | Required. Which app/game to launch. |
bitrate |
number (kbps) | Video bitrate, e.g. 8000 for 8 Mbps. |
fps |
number | Target framerate, e.g. 30, 60, 120. |
videoSize |
720p, 1080p, 1440p, 4k, native, custom |
Stream resolution. native uses the viewing browser's own screen size. |
videoSizeCustom.width |
number | Custom width. Only takes effect with videoSize=custom. |
videoSizeCustom.height |
number | Custom height. Only takes effect with videoSize=custom. |
Anything omitted falls back to your saved settings for that host. Values that aren't valid numbers
are ignored rather than applied, and videoSizeCustom.width/.height are only honoured as a pair —
supplying one without the other changes nothing.
The input-only page takes just the host:
input.html?hostId=1
The config file is under server/config.json relative to the executable.
Here are the most important settings for configuring Moonlight Web.
For a full list of values look into the Rust Config module.
The credentials the Website will prompt you to enter. Change this from the default value to the credentials for the website.
{
"credentials": "your password"
}If you set this null authentication will be disabled and the Authorization header won't be used in requests.
{
"credentials": null
}Once logged in you can enable 2FA (TOTP) from the Settings → Security section.
- Click Set Up Two-Factor Authentication.
- A 32-character base32 secret key is shown. Enter it into your authenticator app using the "Enter setup key" option (Google Authenticator, Authy, etc.). Set the algorithm to SHA1, digits to 6, and period to 30 s.
- Enter the 6-digit code displayed by the app to confirm, then click Ok.
From this point every login requires your password and the current TOTP code.
The secret is saved into server/config.json under the key totp_secret. Active sessions are stored in server/sessions.json and survive server restarts. Sessions expire automatically after 90 days.
To disable 2FA, click Disable Two-Factor Authentication in the same section.
Note: 2FA only protects the web interface. It has no effect on the Sunshine pairing process.
The address and port the website will run on
{
"bind_address": "127.0.0.1:43780"
}If enabled the web server will use https with the provided certificate data
{
"certificate": {
"private_key_pem": "./server/key.pem",
"certificate_pem": "./server/cert.pem"
}
}This will set the port range on the web server used to communicate when using WebRTC
{
"webrtc_port_range": {
"min": 40000,
"max": 40100
}
}A list of ice servers for webrtc to use.
{
"webrtc_ice_servers": [
{
"urls": ["stun:stun.cloudflare.com:3478"]
}
]
}This will advertise the ip as an ice candidate on the web server. It's recommended to set this but stun servers should figure out the public ip.
ice_candidate_type:
host-> This is the ip address of the server and the client can connect tosrflx-> This is the public ip address of this server, like an ice candidate added from a stun server.
{
"webrtc_nat_1to1": {
"ice_candidate_type": "host",
"ips": [
"74.125.224.72"
]
}
}This will set the network types allowed by webrtc.
Allowed values:
- udp4: All udp with ipv4
- udp6: All udp with ipv6
- tcp4: All tcp with ipv4
- tcp6: All tcp with ipv6
{
"webrtc_network_types": [
"udp4",
"udp6",
]
}This is useful when rerouting the web page using services like Apache 2. Will always append the prefix to all requests made by the website.
{
"web_path_prefix": "/moonlight"
}- Thanks a million to @MrCreativ3001 for the base project!
Make sure you've cloned this repo with all it's submodules
git clone --recursive https://github.com/Argon2000/moonlight-web-stream-tsla.gitA Rust nightly installation is required.
There are 2 ways to build Moonlight Web:
-
Build it on your system
When you want to build it on your system take a look at how to compile the crates:
-
Compile using Cargo Cross
After you've got a successful installation of cross just run the command in the project root directory This will compile the web server and the streamer
cross build --release --target YOUR_TARGET
Note: windows only has the gnu target
x86_64-pc-windows-gnu
moonlight-common-sys are rust bindings to the cpp moonlight-common-c library.
Required for building:
- A CMake installation which will automatically compile the moonlight-common-c library
- openssl-sys: For information on building openssl sys go to the openssl docs
- A bindgen installation for generating the bindings to the moonlight-common-c library
This is the web server for Moonlight Web found at moonlight-web/web-server/.
It'll spawn a multiple streamers as a subprocess for handling each stream.
Required for building:
Build the web frontend with npm.
npm install
npm run buildThe build output will be in moonlight-web/web-server/dist. The dist folder needs to be called static and in the same directory as the web server executable.
This is the streamer subprocess of the web server and found at moonlight-web/streamer/.
It'll communicate via stdin and stdout with the web server to negotiate the WebRTC peers and then continue to communicate via the peer.
Required for building:
The Tesla in-car browser has unique constraints that prevent vanilla Moonlight Web from working:
- Blocks local/private IP access — you must use a real domain name
- WebSocket connections fail intermittently (~50% of the time) — this fork auto-retries transparently
- Audio autoplay is blocked — handled with proper AudioContext resume on first interaction
- ICE gathering behaves non-standard — uses non-trickle ICE with timeout fallback
- Gamepad/controller input quirks — mapped correctly for the Tesla browser's Gamepad API implementation
- No WASM decoder needed — Tesla has native H.264/HEVC codec support
- Works in Drive mode — the Tesla browser remains functional while driving; passengers can stream video, games, or any content
Tesla game streaming, Moonlight Tesla, stream PC to Tesla, Sunshine Tesla, remote desktop Tesla, cloud gaming Tesla, Tesla screen PC games, Tesla entertainment while charging, Tesla entertainment while driving, WebRTC Tesla, Moonlight web client, watch YouTube on Tesla, watch Netflix on Tesla, Tesla browser streaming, Tesla video streaming, stream to Tesla touchscreen




