R in Your Browser with WebR
WebR compiles R to WebAssembly. R runs in your browser: no server, no RStudio, no installation. Nothing leaves your machine.
For technical writing where the code is the point, this removes the barrier. Readers can run it, change it, see what happens. The page is the environment.
A few things to know:
- First load takes a few seconds while webR initializes.
ggplot2is pre-loaded for the examples below, which adds a few seconds at startup.- Not every CRAN package has a WASM build, but the most common ones do.
Base R runs immediately
The Collatz sequence converges to 1 from any positive integer. Change 27 to any other starting number:
Old Faithful has a distinctly bimodal eruption pattern. A density estimate shows the two clusters better than a histogram:
The central limit theorem, interactively
Draw n_obs samples from an exponential distribution (right-skewed, decidedly non-normal), compute the sample mean, repeat 2000 times. The orange curve is the normal approximation.
Set n_obs <- 1, then n_obs <- 5, then n_obs <- 30. The distribution of means converges to normal.
ggplot2
ggplot2 loaded in the background while you were reading. Same faithful data, with 2D density contours:
The two clusters are real. Old Faithful has two modes: short eruptions (~2 min) with short waits (~55 min), and long eruptions (~4.5 min) with long waits (~80 min).
Installing packages from CRAN
Any package with a WASM binary can be pre-loaded via the packages key in the document frontmatter. Both ggplot2 and palmerpenguins are loaded that way here, which is why this cell runs without an install.packages() call:
The WASM CRAN mirror covers most of the tidyverse and common packages. Three ways to install:
- Frontmatter
packageskey (used above): packages load before the first cell runs. Best for dependencies your post relies on from the start. webr::install("pkg")in a cell: installs at runtime inside a code cell. Works for packages you want readers to load on demand.install.packages(): works after callingwebr::shim_install(), which patches the function to use the WASM mirror. Useful if you want familiar CRAN-style syntax.
webR can’t compile from source, so packages need a WASM binary. Check repo.r-wasm.org to see what’s available.
Where webR fits
The best case is when the code itself is what you’re teaching. Parameter exploration earns the startup time: the CLT cell above lets readers change n_obs from 1 to 30 and watch the histogram shift. That experience is the lesson in a way that a static image isn’t.
Algorithm exploration is another good fit. Code that behaves differently on different inputs — recursive sequences, graph traversals, sorting algorithms — invites readers to poke at it. The Collatz sequence starting at 27 takes 111 steps. Starting at 837799 takes 524. Readers can find out without leaving the page.
Simulation-based statistical intuition also works well. Bootstrap confidence intervals, permutation tests, birthday problem probabilities: hard to explain with formulas, obvious after running 2000 simulations. WebR is fast enough for this kind of thing that the wait doesn’t become the story.
When to skip it
WebR adds 5–10 seconds of startup and loads the full R runtime into the browser. That cost is worth it only if readers will actually interact with the code.
Skip it when the post is about results. If you’re presenting a plot and explaining what it means, render it statically. The reader gets the information faster, the page works without JavaScript, and there’s nothing to wait for.
Skip it for large data. Everything runs in the browser with no server behind it, which means everything loads into browser memory. A 50 MB CSV will be slow on a laptop and painful on mobile. If you need real data, use a small representative sample or a synthetic dataset.
Skip it for heavy computation. WebR runs in a single browser thread without the optimized BLAS/LAPACK libraries that native R uses. A simulation that takes one second in R might take ten in webR. The CLT demo above works fine. Fitting a random forest on 100,000 rows does not.
Skip it for benchmarking. WebR timing results don’t represent native R performance. If your point involves timing, either note this explicitly or skip webR for that post.