Examples using WebR
Starting webR
Once webR has been downloaded and made available for use, it can be initialised by creating a new instance of the WebR
class.
const webR = new WebR();
A configuration object of type WebROptions
can be passed to the above constructor. This optional argument provides access to advanced webR settings such as WebAssembly binary URLs, communication channel settings, and control over the Emscripten environment that the WebAssembly binaries are to be executed in.
Awaiting initialisation
WebR is ready to use once the promise returned by WebR.init()
resolves. In a modern asynchronous JavaScript environment (e.g. within an async function
), the await
keyword can be used to ensure webR has finished starting before continuing.
await webR.init();
Example Snippets
Evaluating R code and retrieving results
This example demonstrates running R code with WebR.evalR()
and getting the result in the form of an RObject
proxy. The result is then converted into a JavaScript object before being output to the JavaScript console.
let result = await webR.evalR('rnorm(10,5,1)');
let output = await result.toArray();
console.log('Result of running `rnorm` from webR: ', output);
import type { RDouble } from 'webr';
let result = await webR.evalR('rnorm(10,5,1)') as RDouble;
let output = await result.toArray();
console.log('Result of running `rnorm` from webR: ', output);
Result of running `rnorm` from webR: [4.811743371393964,
3.9713199286446246, 5.752161964201946, 4.699964764731866,
6.793839154500917, 5.80036239061935, 5.342100112394279,
5.555879371932934, 4.314676418095938, 5.113592217724398]
The options
argument of WebR.evalR()
has been designed so that the default is sufficient for general R evaluation. However, the default behaviour can be changed by passing an options
argument of type EvalROptions
, if required.
See the Evaluating R Code section for more detailed documentation.
Capturing console output from R code
The following snippet demonstrates using a webR shelter to capture R output during evaluation. This differs from the example above in that the output is retrieved in the form of lines of text from the stdout
stream, rather than a reference to an R object in the form of an RObject
proxy.
Once the result is no longer needed, the shelter is purged for the purpose of memory management.
let shelter = await new webR.Shelter();
let result = await shelter.captureR('print(rnorm(10,5,1))');
console.log('Output obtained from running `rnorm` from webR:')
console.log(result.output);
.purge(); shelter
Output obtained from running `rnorm` from webR:
[
{type: 'stdout', data: ' [1] 5.322551 6.924352 4.592612 6.357413 7.198339 5.152446 4.185396'}
{type: 'stdout', data: ' [8] 4.995504 6.034569 6.295957'}
]
Executing an R function from JavaScript
The following snippet demonstrates executing an R function from JavaScript. A reference to the R function is first obtained as an RFunction
JavaScript proxy, and then executed in a similar way to a normal JavaScript function.
let sin = await webR.evalR('sin');
let result = await sin([1,2,3]);
console.log('Result of running `await sin([1,2,3])`:')
console.log(result.values);
import type { RFunction } from 'webr';
let sin = await webR.evalR('sin') as RFunction;
let result = await sin([1,2,3]);
console.log('Result of running `await sin([1,2,3])`:')
console.log(result);
Result of running `await sin([1,2,3])`:
{
type: 'double',
names: null,
values: [ 0.8414709848078965, 0.9092974268256817, 0.1411200080598672 ]
}
Evaluating R code within an environment
The options
argument of WebR.evalR()
can be used to control how R code is executed. In this example the options argument is used to create a new environment for the R code to be evaluated in.
let result = await webR.evalR('foo + bar', {
env: { foo: 1234, bar: 1, baz: 'abc' }
;
})let output = await result.toNumber();
console.log(output);
import type { RDouble } from 'webr';
let result = await webR.evalR('foo + bar', {
: { foo: 1234, bar: 1, baz: 'abc' }
envas RDouble;
})
let output = await result.toNumber();
console.log(output);
1235
Bind a JavaScript object into the R global environment
The following snippet demonstrates binding a JavaScript object into the R global environment. The JavaScript object is automatically converted into an R object, and the value is then used in the subsequent R evaluation.
await webR.objs.globalEnv.bind('arr', [2,4,6,8,9]);
let result = await webR.evalR('sample(arr, 3)');
let output = await result.toArray();
console.log(output);
import type { RDouble } from 'webr';
await webR.objs.globalEnv.bind('arr', [2,4,6,8,9]);
let result = await webR.evalR('sample(arr, 3)') as RDouble;
let output = await result.toArray();
console.log(output);
[2, 9, 6]
Testing and narrowing R object types
Since functions such as WebR.evalR()
may run arbitrary R code, it is not always clear what type of R object will be returned. To help manage the ambiguity, webR provides type predicate functions for several types of RObject
.
These functions return boolean true
if the argument’s R object type matches, and so different actions can be taken at runtime depending on the type of a given R object. This is particularly useful for TypeScript users, where the compiler is able to use the functions to narrow the R object type automatically.
In this example, an R numeric, character, or list could be returned randomly by the sample
function. Type predicate functions are used to detect and handle each case.
import { isRDouble, isRCharacter, isRList } from 'webr';
let result = await webR.evalR(`
sample( list(50.75, "foo", list(1,2,3)) , 1)[[1]]
`);
if (isRDouble(result)) {
let output = await result.toNumber();
console.log('A number: ', output)
else if (isRCharacter(result)) {
} let output = await result.toString();
console.log('A string: ', output)
else if (isRList(result)) {
} let output = (await result.toArray()).length;
console.log('A list with ', output, 'elements.')
}
Cleaning up after an R object is no longer in use
R object references in the form of an RObject
are subject to memory management and should be destroyed when they are no longer required. This signals to R that the object referenced may be deleted by the garbage collector.
let result = await webR.evalR('rnorm(3,5,1)');
let output = await result.toArray();
console.log(output);
.destroy(result); webR
import type { RDouble } from 'webr';
let result = await webR.evalR('rnorm(3,5,1)') as RDouble;
let output = await result.toArray();
console.log(output);
.destroy(result); webR
[5.373198157561013, 4.493213148077507, 5.134879842881727]
Plotting with a HTML canvas element
See the Plotting section for further information about plotting with webR.
WebR’s support package includes a built in graphics device, webr::canvas()
. When R uses this device, messages are sent to the main thread containing bitmap image data. The image data can then be displayed using a HTML Canvas element on the page.
In this example, R executes a plotting command and webR’s output message queue is then flushed. The image data is retrieved from the output messages and drawn to the HTML Canvas element with fixed id="plot-canvas"
.
<canvas id="plot-canvas" width="1008" height="1008"></canvas>
<script type="module">
import { WebR } from 'https://webr.r-wasm.org/latest/webr.mjs';
const webR = new WebR();
await webR.evalRVoid(`
webr::canvas()
plot(rnorm(1000), rnorm(1000),
xlab="x axis label", ylab="y axis label",
main="An rnorm plot")
dev.off()
`);
const msgs = await webR.flush();
.forEach(msg => {
msgsif (msg.type === 'canvas' && msg.data.event === 'canvasImage') {
const canvas = document.getElementById('plot-canvas');
.getContext('2d').drawImage(msg.data.image, 0, 0);
canvaselse {
} console.log(msg);
};
})</script>
Capturing plots while evaluating R code
When evaluating R code using captureR()
, plots generated by the webr::canvas()
graphics device may be captured and stored for display later, rather than immediately issued as an output message as in the previous example.
Once captured, the image data can be retained and displayed later using a HTML Canvas element on the page.
// Evaluate R code, capturing all output
const shelter = await new webR.Shelter();
const capture = await shelter.captureR("hist(rnorm(10000))");
// Draw the first (and only) captured image to the page
if (capture.images.length > 0) {
const img = capture.images[0];
const canvas = document.getElementById("plot-canvas");
.width = img.width;
canvas.height = img.height;
canvas.getContext("2d").drawImage(img, 0, 0, img.width, img.height);
canvas
}
.purge(); shelter
// Evaluate R code, capturing all output
const shelter = await new webR.Shelter();
const capture = await shelter.captureR("hist(rnorm(10000))");
// Draw the first (in this case only) captured image to the page
if (capture.images.length > 0) {
const img = capture.images[0];
const canvas = document.getElementById("plot-canvas") as HTMLCanvasElement;
.width = img.width;
canvas.height = img.height;
canvas.getContext("2d")!.drawImage(img, 0, 0, img.width, img.height);
canvas
}
.purge(); shelter
Fully worked examples
Creating an interactive webR REPL console
The following HTML document loads webR from CDN and creates a simple interactive R console. This demonstrates using the Console
class with JavaScript callbacks to interface with the built-in R REPL.
<html>
<head>
<title>WebR Test Console</title>
</head>
<body>
<div>
<pre><code id="out">Loading webR, please wait...</code></pre>
<input spellcheck="false" autocomplete="off" id="input" type="text">
<button onclick="globalThis.sendInput()" id="run">Run</button>
</div>
<script type="module">
/* Create a webR console using the Console helper class */
import { Console } from 'https://webr.r-wasm.org/latest/webr.mjs';
const webRConsole = new Console({
stdout: line => document.getElementById('out').append(line + '\n'),
stderr: line => document.getElementById('out').append(line + '\n'),
prompt: p => document.getElementById('out').append(p),
;
}).run();
webRConsole
/* Write to the webR console using the ``stdin()`` method */
let input = document.getElementById('input');
.sendInput = () => {
globalThis.stdin(input.value);
webRConsoledocument.getElementById('out').append(input.value + '\n');
.value = "";
input
}
/* Send input on Enter key */
.addEventListener(
input"keydown",
=> {if(evt.keyCode === 13) globalThis.sendInput()}
(evt) ;
)</script>
</body>
</html>
See Serving Pages with WebR for further details about web server requirements for web pages that load webR.
After loading, the resulting web page should present an interactive R console similar to the following,
Loading webR, please wait...
Interactive charts with ggplot2
and plotly
Using the plotly
R package, ggplot2
output can be converted into interactive figures powered by plotly.js.
<html>
<head>
<title>Plotly Example</title>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/plotly.js/2.26.2/plotly.min.js"
charset="utf-8"
></script>
</head>
<body>
<div>
<pre><code id="out">Loading webR, please wait...</code></pre>
</div>
<script type="module">
import { WebR } from 'https://webr.r-wasm.org/latest/webr.mjs';
const webR = new WebR({ interactive: false });
await webR.init();
const outElem = document.getElementById('out');
.innerText = 'Loading plotly, please wait...';
outElemawait webR.installPackages(['jsonlite', 'ggplot2', 'plotly'], true);
.innerText = 'Generating plot, please wait...';
outElemconst plotlyData = await webR.evalRString(`
library(plotly)
library(ggplot2)
p <- ggplot(mpg, aes(displ, hwy, colour = class)) +
geom_point()
plotly_json(p, pretty = FALSE)
`);
.replaceChildren();
outElem.newPlot('out', JSON.parse(plotlyData), {});
Plotly</script>
</body>
</html>
Integrating webR with other frameworks
See the example repositories below for examples of integrating webR into other JavaScript/TypeScript systems and frameworks.