Shiny on webR demo
This repo demonstrates the deployment of a Shiny app running on webR in the browser, hosting the app bundle via Netlify. The resulting app can be seen at https://shiny-standalone-webr-demo.netlify.app.
The webR binaries are loaded through the public CDN hosted at https://webr.r-wasm.org. A pre-compiled webR package repository (containing various R package binaries built for WebAssembly) is additionally included as part of the deployment in the repo
directory. The packages, including Shiny, are loaded into the page once webR has been initialised. This package repository has been built using the experimental webr-repo
scripts at https://github.com/r-wasm/webr-repo.
How it works
The httpuv package included here is an experimental build of a minimal httpuv shim package, designed for running under the restrictions of WebAssembly in the browser, where there is no raw socket access. The R functions that would normally be executed when a HTTP request arrives are instead made available for use in the web page loading webR through webR's JavaScript API.
In this demo the page first loads webR, then starts a JavaScript Service Worker script essentially acting like a network proxy server. Network requests under a special URL are intercepted by the service worker and forwarded to the webR session running in the page.
In webR, a Shiny app is started making use of the httpuv shim package, now listening for the forwarded requests. The resulting responses from the running shiny session are emitted as webR output messages over the usual webR communication channel.
The service worker is also listening for resulting output messages, and when they are received it responds to the associated intercepted browser network requests with responses from the running Shiny session.
Once the above has all been set up, an iframe
HTML element is dynamically added to the page pointing to the special intercepted URL. This frame displays the client-side of the Shiny app running under webR.
Note: There are other similar ways to orchestrate this communication process. For example, Shinylive implements service worker communication using the MessagePort
API.
Limitations
Since this technique depends on loading a service worker into the page, the standard webR service worker communication channel cannot be used. This is because a web page is not able to use multiple service worker scripts at the same time. The page should be served with cross-origin isolation HTTP headers set, so that the default (and recommended) shared array buffer channel can be used instead. In this demo, the headers are configured for hosting via Netlify in the file netlify.toml
.
If use of the webR service worker communication channel is strictly required, it is possible to do so by using a custom webr-serviceworker.js
script when loading webR. The custom service worker should first load the usual webR service worker script with importScripts
, and then include additional handling for httpuv traffic by registering further handlers for fetch
and message
events using self.addEventListener
. Note that this demo does not include such an additional setup and will only work when served with the required cross-origin isolation headers.
A short warning
The latest build of webR and the httpuv shim package that have been used in this demo are experimental and may be subject to change at any time. The latest version of the webR documentation can be found at https://docs.r-wasm.org/webr/latest/.