R in the Browser: Announcing Our WebAssembly Distribution
R is now available in emscripten-forge, enabling the Xeus-R kernel in JupyterLite

In the past few months, QuantStack has received funding from the Bill & Melinda Gates Foundation through a grant to CourseKata to develop support for the R programming language in JupyterLite and the Emscripten-forge distribution for WebAssembly.
Today, we are thrilled to announce the availability of the R programming language in the Emscripten-Forge distribution for WebAssembly, which enables R workflows in JupyterLite through the Xeus-R Jupyter kernel.
To experience R in JupyterLite, simply click on the link below:
In this article, we present the details of our efforts to enable R workflows in JupyterLite. We cover the challenges encountered in packaging R and its dependencies for this platform, the solutions we implemented, and the process of handling R mamba packages on the frontend.
Why WebAssembly? Scaling scientific computing workflows in Jupyter to millions of users
The main challenge in scaling Jupyter deployments lies in providing a live computing environment on the server side for each user session.
While Kubernetes-based JupyterHub deployments have been successfully implemented at a university scale (such as the UC Berkeley Data 8 class, or the JupyterHub deployment of Université Paris Saclay), they demand substantial computing resources and a skilled DevOps team to achieve and maintain this level of scalability.
In contrast, JupyterLite represents a fundamentally different paradigm. As a browser-based distribution of Jupyter, it runs language kernels directly in the browser, enabling significantly greater scalability and ease of deployment. Notable JupyterLite-based applications include:
- A JupyterLite console integrated into NumPy’s front page, providing immediate access to NumPy functionality without requiring installation.
- A similar console application on SymPy’s project page, fully hosted statically.
- The official Jupyter website¹.
- The examples in Scikit-Learn’s documentation.
These examples illustrate how WebAssembly can be transformative by providing ready-to-use computing environments for scientific workflows, in documentation, blog posts, and scientific communications. The WebAssembly environments are made available to the hundreds of thousands of monthly visitors of these websites without requiring any computing resources on the backend.
[1] Even though it powers the deployment of Jupyter on the main Jupyter website, JupyterLite is not an officially sanctioned Jupyter project.
Beyond Python, enabling R in the browser, and more
While the initial focus of the JupyterLite team has been on the Python programming language, the JupyterLite infrastructure was designed to be language-agnostic from the outset. The same principle applies to the package management stack we have been contributing to over the past few years, namely, mamba and conda-forge.
When we began targeting the WebAssembly platform, we chose to build our efforts on the mamba and conda-forge ecosystem. This decision allowed us to provide customizable environments for WebAssembly and expand beyond Python. This enabled applications such as the JupyterLite terminal, created by Ian Thomas, which comprises a bash-like shell written in TypeScript called Cockle, and WebAssembly builds of native shell commands such as cat, grep, ls, and vim. You can read the earlier announcement introducing the JupyterLite terminal.
Today, we are excited to reveal the availability of R in emscripten-forge, enabling Jupyter kernels such as Xeus-R to be used in JupyterLite.
With the addition of R, emscripten-forge now provides a unified packaging solution for Python, R, and terminal applications for WebAssembly. This covers the scopes of Pyodide (a Python distribution for WebAssembly), WebR (an R distribution for WebAssembly), and Sandbox.bio (a shell emulator with WebAssembly commands).
Building R for Emscripten-forge
Emscripten-forge is a GitHub organization containing recipes to build conda packages for the emscripten-wasm32 platform; it is also the first distribution of conda packages targeting this platform. It is built upon a modernized stack, leveraging rattler-build (the successor to the conda-build package builder) and mamba. The emscripten-forge project, which was created and is still led by Thorsten Beier, is now a team effort covering a broad range of packages.
In order to build R with emscripten-forge, we first required a toolchain capable of converting Fortran and C/C++ code into WebAssembly. Since R relies on BLAS and LAPACK (which are written in Fortran) and many essential R packages wrap native libraries that use Fortran, this conversion capability was crucial.
Fortran
By default, the emscripten-forge toolchain can cross-compile any C/C++ packages into WebAssembly; however, the first challenge in building the R stack was expanding the toolchain to also compile Fortran code. The two compilers that we considered for this task are LFortran and Flang.
- LFortran is a compiler built on top of LLVM and designed from the ground up as a cross-compiler. Despite its potential, LFortran is still in its early stages compared to Flang and does not yet fully support all Fortran features.
- LLVM Flang is the Fortran compiler of the LLVM project. Unfortunately, it does not support cross-compilation.
Working with Serge Guelton and Kerim Birgi, we initially experimented with the LFortran compiler and contributed to addressing issues encountered when building Netlib LAPACK. The BLAS implementation from this package served as a valuable testing ground since BLAS is one of the core dependencies for R.
After careful consideration, we found that Flang, with its ability to compile a wider variety of Fortran projects, brought us closer to building the Fortran components of R to WebAssembly. George Stagg, the main developer of the WebR project, has written a detailed article on using Flang to produce WebAssembly, which was adapted to the (then) latest version of LLVM (v18). Inspired by George Stagg’s endeavors, Serge posted upstream contributions to the Flang project (#99465, #99822, #101242, #105589), resolving issues with 32-bit platforms that were impacting our effort to support WebAssembly. All of these changes will be included in LLVM/Flang 20.0.
The remaining patches which cannot be merged upstream at this point involve:
- (1) enabling code generation that targets WebAssembly (198c0e1),
- (2) encoding target-specific size definitions for various data types given that the host and target platforms have different architectures (1da66cd), and
- (3) disabling support for 128-bit floating-point types (f20c7c0).
A version of (2) that could be contributed upstream is in the works, which would be one step closer to making WebAssembly an official target for Flang. Additionally, (3) will no longer be needed starting with Flang v20.
These patches were integrated by Axel Obermeier into the Flang build recipe for the conda-forge distribution, making the modified build accessible to everyone. It can be installed on Linux with mamba or micromamba by executing:
micromamba install conda-forge/label/emscripten::flang libllvm19 --no-channel-priority
The final ingredient for our toolchain involved creating a compatible Fortran runtime library. Since Flang implements its runtime library in C/C++, compiling this library with Emscripten proved to be straightforward. We added a recipe for libflang to emscripten-forge, which packages the runtime library and includes all the aforementioned patches.
In the following example, we invoke Flang on a simple Fortran script, and execute the resulting JavaScript and WebAssembly with NodeJS.
> ls
hello.f90 libFortranRuntime.a
> cat hello.f90
File: hello.f90
program hello
print *, “Hello, Fortran!”
end program hello
> source ~/emsdk/emsdk_env.sh
> flang-new --target=wasm32-unknown-emscripten -c hello.f90 -o hello.o
> emcc hello.o libFortranRuntime.a -o hello.js
> ls
hello.f90 hello.js hello.o hello.wasm libFortranRuntime.a
> node hello.js
Hello, Fortran!
With a working Fortran-to-WebAssembly toolchain, we managed to compile BLAS and LAPACK (See the LAPACK recipe). But before we could get started with building R, we needed to tackle the remaining R dependencies.
Walking up the stack
In R (v4.4), there are a handful of dependencies that are required to provide basic functionality; these are: libiconv, zlib, bzip2, xz, pcre2, and libcurl. Thanks to the packaging efforts led by Thorsten Beier and Wolf Vollprecht, these dependencies are available on emscripten-forge with the exception of libcurl. Because of its socket-based networking architecture and reliance on system-level resource access, libcurl faces fundamental incompatibilities with WebAssembly’s sandboxed environment constraints. Therefore, libcurl had to be disabled in the R source code. As a consequence, downstream R packages which depend on libcurl cannot be ported to WebAssembly until a suitable libcurl replacement is available.
Apart from the core dependencies, R also requires a BLAS implementation. Although external BLAS libraries can be used with R, the R code base contains linear algebra routines from Netlib BLAS and a subset of LAPACK where both libraries are written in C and Fortran. To simplify configuration, we opted for these internal implementations instead of using external BLAS and LAPACK libraries.
Furthermore, several key optional dependencies were cross-compiled to WebAssembly to enhance R’s graphical capabilities; these include libpng, libtiff, cairo, and pango, along with their respective subdependencies. One particularly challenging aspect of packaging these libraries was the cross-compilation of glib, which both cairo and pango depend on. Compiling glib was only possible thanks to the efforts of Kleis Auke Wolthuizen whose patches made glib compatible with Emscripten, and Johan Mabille who disabled multi-threading.
The R dependencies are summarized in the following table:

Cross-compilation of R
Although R typically configures and builds seamlessly on most unix platforms, cross-compiling is not straightforward because R uses a bootstrap approach; first it creates a minimal version of R which is then used to run several R scripts to complete the build process. Bootstrapping is a challenge because the built binaries for the target platform (WebAssembly) are not executable (at least not directly) from the host platform. Therefore, the r-base package necessitates two build phases. First, we compile R for the host platform (Linux) with GCC and Flang; this generates the R and Rscript executables needed for the second phase. And then, we cross-compile to WebAssembly with Emscripten and Flang.
One of the major challenges of this setup was generating the R Data Base (.rdb) and R Data Index (.rdx) files for the internal packages of r-base (base, compiler, grDevices, graphics, grid, methods, parallel, splines, stats, stats4, tools, and utils). To accomplish this, it was necessary to dynamically load some of the internal shared libraries (tools, grDevices, graphics, utils, stats, and methods) which provide essential functionality to generate the data and index files. However, the R executables we created during the first phase of the build are incompatible with the WebAssembly shared libraries. In order to work around this, we temporarily replaced the WebAssembly libraries of the internal packages with the linux shared libraries we built during the first phase (See cross_libraries.sh). This makeshift solution is also used to cross-compile all other R packages; the setup is carried out by the activation script of the cross-r-base package (See activate-cross-r-base.sh).
Additionally, the R source code required a few modifications to enable cross-compilation to WebAssembly.
- The Emscripten platform was added to select the correct configuration for the platform.
- The dependency on libcurl was removed (as mentioned in the previous section) and as a consequence, the internet package which relies on libcurl was disabled.
- System calls which are not compatible with our WebAssembly environment were disabled.
- Cairo replaced Xlib as the default bitmap type to enable graphics.
The complete list of patches applied to R is available here: r-base patches.
The R package ecosystem
Building R itself was just the beginning. The next challenge was to provide a wide array of R packages to create a robust development environment. Fortunately, many packages are written in pure R and are available as “noarch” packages on conda-forge, making them directly compatible with emscripten-forge environments. However, for the R packages that require compilation, a package recipe needs to be created and added to emscripten-forge. Thanks to the help of Anutosh Bhat, the following packages have been compiled and are available to use.
r-askpass r-digest r-haven r-magrittr r-purrr r-tidyr
r-base64enc r-dplyr r-hexbin r-mass r-rcpp r-tzdb
r-bit r-ellipsis r-htmltools r-matrix r-readr r-utf8
r-bit64 r-fansi r-isoband r-mgcv r-rlang r-vctrs
r-cachem r-farver r-jsonlite r-mime r-sp r-vroom
r-cli r-fastmap r-later r-nlme r-stringi r-xfun
r-colorspace r-ggrepel r-lattice r-plyr r-sys r-yaml
r-data.table r-glue r-lazyeval r-promises r-tibble
Most of the time, cross-compiling an R package is very simple because the bulk of the work is handled by r-base. Building a package typically boils down to a single line:
$R CMD INSTALL $R_ARGS .
Occasionally, minor patches are applied to the package source code to enable the WebAssembly target platform or to simplify cross-compilation. Some R packages, such as r-nlme, contain Fortran code; for such packages, the custom flang compiler needs to be installed before the package can be cross-compiled. Should any package be needed for your use case, feel free to contribute them to emscripten-forge!
Xeus-R
Once a sufficient set of packages is available for the platform, the next step is to provide a development environment for end users. Our chosen tool for this purpose is Jupyter.
The most popular Jupyter kernel for R is IRkernel, which has been developed over several years. It includes complete ZeroMQ bindings for the R language and comprehensive coverage of the Jupyter protocol. However, since IRkernel is tied to ZeroMQ, we opted to work with the Xeus-R project instead, which was announced last year. Xeus-R was developed by Romain François in collaboration with our team and with JupyterLite in mind. It is built upon the Xeus library, which provides a native implementation of the Jupyter protocol and is adapted to work with the JupyterLite frontend.
Fortunately, the components of IRkernel responsible for the rich representation of R objects and rich display in Jupyter frontends were split into two separate R packages: IRdisplay and repr. These packages can be reused in Xeus-R. As a result, Xeus-R and IRkernel offer the same rich display functionality, ensuring that Jupyter notebooks created with one kernel will work with the other. However, Xeus-R provides a different implementation of the communication layer through jupyterlite-xeus, which enables integration with the JupyterLite stack. This integration was previously implemented for Xeus-Python and Xeus-Lua.
One key aspect of this integration is the populating of the in-memory file system used by JupyterLite kernels with the required runtime dependencies, particularly shared libraries. This process is done upon bootstrapping the kernel. For this purpose, Anastiasiia Sliusar and Martin Renou developed mambajs, which, in combination with jupyterlite-xeus, processes conda packages in the frontend and installs them in the kernel filesystem at startup. In WebAssembly, shared libraries need to be handled with special care in order to function as expected. Drawing inspiration from the Python community, we adapted the pioneering work of Pyodide to properly load shared libraries into the filesystem.
How to make a deployment with custom packages
The GitHub repository https://github.com/jupyterlite/xeus-lite-demo is a template for creating a JupyterLite deployment on GitHub pages that includes the packages specified in a conda environment.
The process is as follows:
- Create a new repository from the GitHub template.
- Enable the deployment on GitHub pages from a GitHub action, as shown in the README.
- Edit the environment file to include the desired packages.
For example, to deploy an R kernel with coursekata installed, the environment.yml file would contain the following:
name: xeus-r
channels:
- https://repo.mamba.pm/emscripten-forge
- conda-forge
dependencies:
- xeus-r
- r-coursekata
The deployment linked at the beginning of this article was created with this template.
How to contribute
Contributors are always welcomed! New R packages can be requested by opening an issue on emscripten-forge, or added directly by opening a pull request with the desired package recipe. For instructions on how to contribute, please visit https://emscripten-forge.org/.
What is in the works
Currently, we are developing the infrastructure to dynamically install emscripten-forge and conda-forge noarch packages in JupyterLite kernel environments. This will apply to all Xeus kernels that make use of these packages, such as Xeus-Python, Xeus-R, and Xeus-Lua. In the case of R, this may be directly bound to install.packages.
Additionally, we are working on more advanced features for the R kernel, including support for Jupyter interactive widgets and the split of the pure R parts of xeus-r into a separate package, which would facilitate a possible convergence with IRkernel.
Finally, other language kernels based on the same package management stack are also in the works. Stay tuned for future announcements!
About the Author
Isabel Paredes, who led the charge on bringing R to emscripten-forge, is a senior scientific software developer at QuantStack. Prior to working on this project, she focused on porting the Robot Operating System (ROS) framework to WebAssembly.
Acknowledgments
This effort brought together several endeavors from many open-source developers.
- JupyterLite, the Jupyter distribution that runs entirely in the web browser, was created by Jeremy Tuloup.
- Xeus, the C++ library implementing the Jupyter kernel protocol, enabling a custom communication layer, and is foundational to kernels like xeus-r, xeus-python, running in JupyterLite, was created by Johan Mabille and is maintained by a broader team including Martin Renou, Sylvain Corlay, and Thorsten Beier, who worked on the first integration with JupyterLite.
- Emscripten-forge, the distribution of conda packages for WebAssembly, was created by Thorsten Beier, who continues to lead the project. Many recipes were contributed by Isabel Paredes (for the R ecosystem), but also Kerim Birgi, Anutosh Bhat, Martin Renou, Wolf Vollprecht, and Johan Mabille.
- Xeus-R, the Xeus-based Jupyter kernel for R, was created by Romain François.
- IRDisplay and repr are pure R packages providing rich display and rich mime type rendering of many core R types, which are used in xeus-r. These two packages stem from the IRkernel project, a Jupyter kernel for R written in R, which was started by Thomas Kluyver, and has been maintained over the years by Philipp Angerer and Jan Katins.
- MambaJS, the library enabling the processing of conda packages in the frontend, was developed by Anastasiia Sliusar and Martin Renou.
- Conda-forge, the main distribution of conda packages, includes a large number of “noarch” recipes, including for R packages, and is developed by a broad community of contributors worldwide.
- WebR, an R distribution for WebAssembly, was created by George Stagg. Although not directly used in this project, it documented numerous patches and changes to the Flang compiler that were adapted for this effort.
Funding
The work by Isabel Paredes and other QuantStack team members on bringing R and Xeus-R to Emscripten-Forge was supported by the Bill & Melinda Gates Foundation through a grant to CourseKata. CourseKata is a nonprofit project dedicated to improving statistics and data science education through interactive, research-backed curricula that integrate professional tools like R and Jupyter notebooks. This grant was part of CourseKata’s broader mission to scale its innovative curriculum, equipping students with the skills and confidence to engage with statistics, data science, and coding in meaningful ways.