An unofficial Moonlight Client allowing you to stream your pc to the Web. It hosts a Web Server which will forward Sunshine traffic to a Browser using the WebRTC Api.
- Features that only work in a Secure Context -> How to configure a Secure Context / https
- Controllers: Gamepad API
- Keyboard Lock (allows to capture almost all keys also OS Keys): Experimental Keyboard Lock API
- Web Socket Transport because of the Web Codecs Api
You can install it manually or with docker
-
Install Sunshine
-
Download the compressed archive for your platform and uncompress it or build it yourself
-
Run the "web-server" executable
-
Go to
localhost:8080and view the web interface. You can also the change bind address.
-
Add a new user by typing in your name and password. The first user to login will be created and will be the admin.
-
Add a new pc (
) with the address as
localhostand leave the port empty (if you've got the default port) -
Pair your pc by clicking on the host (
). Then enter the code in sunshine
-
Launch an app
- Forward the web server port on your router (default is 8080, http is 80, https is 443). You can see this in the config as the
bind_address
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:stun.l.google.com:19302",
"stun:stun.l.google.com:5349",
"stun:stun1.l.google.com:3478",
"stun:stun1.l.google.com:5349",
"stun:stun2.l.google.com:19302",
"stun:stun2.l.google.com:5349",
"stun:stun3.l.google.com:3478",
"stun:stun3.l.google.com:5349",
"stun:stun4.l.google.com:19302",
"stun:stun4.l.google.com:5349",
]
},
{
"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": 40010
}
}
}-
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.
- You'll need a private key and a certificate.
You can generate a self signed certificate with this python script moonlight-web/web-server/generate_certificate.py:
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 using the certificates
{
"web_server": {
"certificate": {
"private_key_pem": "./server/key.pem",
"certificate_pem": "./server/cert.pem"
}
}
}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}${MOONLIGHT_SUBPATH}/api/host/stream
ProxyPassReverse ws://${MOONLIGHT_STREAMER}${MOONLIGHT_SUBPATH}/api/host/stream
</Location>
ProxyPass ${MOONLIGHT_SUBPATH}/ http://${MOONLIGHT_STREAMER}${MOONLIGHT_SUBPATH}/
ProxyPassReverse ${MOONLIGHT_SUBPATH}/ http://${MOONLIGHT_STREAMER}${MOONLIGHT_SUBPATH}/
- Enable the created config file
sudo a2enconf moonlight-web- Change config to include the prefixed path
{
"web_server": {
"url_path_prefix": "/moonlight"
}
}- Use https with a certificate (Optional)
Authentication with a reverse proxy works by the proxy adding custom headers to the request of the user. In this example the username header is named X-Forwarded-User.
Make sure that the header is not changeable by any external request and only the proxy can set this header.
Enable proxy authentication by setting the forwarded header username option. By default the auto create missing user option is turned on even if it's not specified in the config.
{
"web_server": {
"forwarded_header": {
"username_header": "X-Forwarded-User",
"auto_create_missing_user": true
}
}
}The config file is under server/config.json relative to the executable.
Here are the most important settings for configuring Moonlight Web.
Most options have command line arguments or environment variables associated with them.
./web-server helpFor a full list of values look into the Rust Config module.
The address and port the website will run on
{
"web_server": {
"bind_address": "0.0.0.0:8080"
}
}The user id which is selected by default when providing no login. Go into the Admin Panel and look for the user id of the user you want to make the default.
{
"web_server": {
"default_user_id": 1284358932
}
}If enabled the web server will use https with the provided certificate data
{
"web_server":{
"certificate": {
"private_key_pem": "./server/key.pem",
"certificate_pem": "./server/cert.pem"
}
}
}This will overwrite the default config of any new browser to open the website.
This will set the port range on the web server used to communicate when using WebRTC
{
"webrtc": {
"port_range": {
"min": 40000,
"max": 40010
}
}
}A list of ice servers for webrtc to use.
{
"webrtc": {
"ice_servers": [
{
"urls": [
"stun:stun.l.google.com:19302",
"stun:stun.l.google.com:5349",
"stun:stun1.l.google.com:3478",
"stun:stun1.l.google.com:5349",
"stun:stun2.l.google.com:19302",
"stun:stun2.l.google.com:5349",
"stun:stun3.l.google.com:3478",
"stun:stun3.l.google.com:5349",
"stun:stun4.l.google.com:19302",
"stun:stun4.l.google.com:5349",
]
}
]
}
}You can also set ice servers with environment variables:
ENV WEBRTC_ICE_SERVER_0_URL=stun:stun.l.google.com:5349
ENV WEBRTC_ICE_SERVER_0_USERNAME=name
ENV WEBRTC_ICE_SERVER_0_CREDENTIAL=cred
ENV WEBRTC_ICE_SERVER_1_URL=stun:stun1.l.google.com:5349will currespond to the ice server
{
"webrtc": {
"ice_servers": [
{
"urls": [
"stun:stun.l.google.com:19302",
],
"username": "name",
"credential": "cred"
},
{
"urls": [
"stun:stun1.l.google.com:5349"
]
}
]
}
}On first startup you can disable all default ice servers with the cli argument --disable-default-webrtc-ice-servers or the environment variable DISABLE_DEFAULT_WEBRTC_ICE_SERVERS.
After the config.json has been generated all ice server in it will be used, even if those are the defaults.
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"
]
}
}
}You can also use the cli argument --webrtc-nat-1to1-host or environment variable WEBRTC_NAT_1TO1_HOST to use a ip as a host candidate type. This will do the same as the json above.
ENV WEBRTC_NAT_1TO1_HOST=74.125.224.72This 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_server": {
"url_path_prefix": "/moonlight"
}
}The header that will give the authenticated username to this web server.
{
"web_server": {
"forwarded_header": {
"username_header": "X-Forwarded-User"
}
}
}Automatically create a new user when the requested user specified in the username_header is not found.
{
"web_server": {
"forwarded_header": {
"auto_create_missing_user": true
}
}
}-
Some config options have changed so backup your old config by renaming it to something like
old_config.json. -
Start the web server which will generate the new config.
-
Move your configurations to the new config
-
The first user to login will be created and will be an admin. All previously stored hosts will be moved to this user.
Other changes:
- Proxy path changed:
- change all instances of
ProxyPass ${MOONLIGHT_SUBPATH}/ http://${MOONLIGHT_STREAMER}/
toProxyPass ${MOONLIGHT_SUBPATH}/ http://${MOONLIGHT_STREAMER}${MOONLIGHT_SUBPATH}/ - Proxying via Apache 2
- change all instances of
- Thanks to @Argon2000 for implementing a canvas renderer, which makes this run in the Tesla browser.
- Thanks to @Maneetbal for creating a new beautiful GUI.
- Thanks to @chromaticpipe for making Github CI.
- Thanks to @qiin2333 for implementing HDR support.
Make sure you've cloned this repo with all it's submodules
git clone --recursive https://github.com/MrCreativ3001/moonlight-web-stream.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:

{ "default_settings": { // possible values: "left", "right", "up", "down" "sidebarEdge": "left", "bitrate": 10000, "packetSize": 2048, "fps": 60, "videoFrameQueueSize": 3, // possible values: "720p", "1080p", "1440p", "4k", "native", "custom" "videoSize": "custom", // only works if videoSize=custom "videoSizeCustom": { "width": 1920, "height": 1080 }, // possible values: "h264", "h265", "av1", "auto" "videoCodec": "h264", "forceVideoElementRenderer": false, "canvasRenderer": false, "playAudioLocal": false, "audioSampleQueueSize": 20, // possible values: "highres", "normal" "mouseScrollMode": "highres", "controllerConfig": { "invertAB": false, "invertXY": false, // possible values: null or a number, example: 60, 120 "sendIntervalOverride": null }, // possible values: "auto", "webrtc", "websocket" "dataTransport": "auto", "toggleFullscreenWithKeybind": false, // possible values: "standard", "old" "pageStyle": "standard" } }