Communication Channels

Using the channel-type meta variable of the webr filter

Author

James Joseph Balamuta

Published

September 15, 2023

Modified

July 1, 2024

As you dive deeper into using webR, it’s crucial to grasp how webR manages communication between R and your web browser. Think of it as a conversation between two active workers or “threads”:

  1. Web Browser: This is your web browser, like Chrome or Firefox, which you use to surf the internet and interact with web pages.
  2. webR: This is like a dedicated helper that specializes in running R code. It operates separately from your main web browser.

Now, here’s why this separation is so important: webR’s special version of R can tackle complicated and time-consuming calculations without causing your web browser to freeze or become unresponsive when you’re on a web page that uses webR.

Imagine trying to watch a video online while your computer is running a heavy software update. Without separating them, your video might start buffering, freeze, or even crash. But by putting the update in the background (on a separate worker, like webR), your video can continue to play smoothly. It’s the same concept with webR and your web browser – keeping things running smoothly without hiccups.

For more details, please see the official webR documentation on Worker Communication and Serving Web Pages with webR.

Communication Channels: How Web Browser and webR Talk

Now, let’s explore how your main web browser and the webR worker thread communicate with each other. They use what we call “communication channels” to swap information. Think of it like like passing notes between two people, but in a high-tech way.

There are a few different types of communication channels available:

  1. “automatic” or 0 (Default):
    • Requirements: Your setup should be prepared for either "shared-array-buffer" or "post-message".
    • Limitations: It’s not clear which option is being used.
  2. “shared-array-buffer” or 1:
    • Requirements: This option requires something called “cross-origin isolation,” which you can read more about here.
    • Limitations: There aren’t any significant limitations to worry about.
  3. “service-worker” or 2:
    • Requirements: You need to have JavaScript service workers set up, like webr-serviceworker.js and webr-worker.js.
    • Limitations: The script for the service worker must be on the same website as the one using WebR.
  4. “post-message” or 3:
    • Requirements: No special requirements are needed for this option.
    • Limitations: R code can’t be interrupted easily. Some advanced R features, like nested R REPLs, might not work as expected.

When you start using WebR, it usually picks the best communication channel for you. However, you can manually choose a channel type if needed. Just remember that each option comes with its own set of requirements and limitations, so pick the one that suits your project best.

In simpler terms, it’s like choosing the right tool for the job. Depending on what you’re doing, you might use different methods to pass messages between your web browser and webR.

Specifying How webR Communicates

In a Quarto document’s YAML header, you can tell webR which communication channel to use by setting the channel-type option. It’s like telling webR how you want it to talk with your web browser. For example, if you want to use the "post-message" channel, you can do it like this:

---
title: "Setting Up webR to use the PostMessage Channel"
format: html
webr:
  channel-type: "post-message"
filters:
- webr
---

Your Communication Channel Choices

The remainder of the document describes the different communication methods alongside ways to satisfy the requirements.

Using “automatic” for Communication (Default)

By default, the quarto-webr extension guides webR to use the "automatic" option for channel-type, if you don’t specify the channel-type in your document’s YAML header. Let’s break down how this default setting works:

  1. Communication Attempts: webR will try two different communication channels in order:
    • First, it attempts to establish a communication channel using "shared-array-buffer".
    • If that doesn’t work, it will then try to use "post-message".
  2. Fallback Behavior: If both of these attempts are unsuccessful, webR code cells in your document will be shown in a deactivated state. This means they won’t run or execute any R code.
Note

It’s important to note that the "automatic" option doesn’t try to use the "service-worker" option for communication.

In summary, when you leave the channel-type unspecified, webR will follow the "automatic" option, attempting to use "shared-array-buffer" and then "post-message". If both attempts fail, your webR code cells will be inactive.

Using “shared-array-buffer” for Communication

Now, let’s explore the "shared-array-buffer" option for communication. When you specify channel-type as "shared-array-buffer", webR aims to use something called SharedArrayBuffer. This choice, however, comes with some specific requirements and benefits:

Requirements:

  • Your web server needs to send web pages with webR using specific HTTP headers. This is to ensure that the page is cross-origin isolated. In simple terms, your server must be set up to allow this kind of communication otherwise webR will not work.

Benefits:

  • The benefit of using this approach is that webR runs notably faster. It’s like giving webR a high-speed lane for its operations.
Note

It’s important to note that the "shared-array-buffer" option isn’t currently available on platforms like GitHub Pages or Quarto Pub. If you’re using these services, we recommend using the channel-type: "post-message" option instead. There’s a possibility that GitHub Pages may offer the option to set the necessary headers in the future, as discussed here.

To help you set up the necessary headers for cross-origin isolation, we provide some guidance for both Netlify and nginx web server administrators:

For Netlify Configuration:

If you’re hosting your website with Netlify, you can add the following code to your netlify.toml configuration file:

[[headers]]
  for = "/directory/with/webr/content/*"

  [headers.values]
    Cross-Origin-Opener-Policy = "same-origin"
    Cross-Origin-Embedder-Policy = "require-corp"

For nginx Web Server Administrators:

If you’re managing a server with nginx, you can use the add_header directive in your server’s configuration file, which is usually found at /etc/nginx/nginx.conf. Here’s an example:

server {
  # Enable headers for the webr directory
  location ^~ /directory/with/webr/content {
    add_header "Cross-Origin-Opener-Policy" "same-origin";
    add_header "Cross-Origin-Embedder-Policy" "require-corp";
  }
}

By following these instructions, you’ll ensure that your web server is set up to display web pages with a cross-origin isolated status, allowing you to use the "shared-array-buffer" option effectively.

Using “service-worker” for Communication

Here, we’ll dive into the "service-worker" option for communication.

Warning

The "service-worker" option doesn’t work with Quarto Pub. If you’re hosting documents with Quarto Pub, please use the channel-type: "post-message" option instead. There’s an ongoing effort to address the service worker upload issue with the Quarto team, which you can track here.

When you set channel-type to "service-worker", webR changes its communication channel to use the Service Worker API. This means you need two worker scripts, webr-worker.js and webr-serviceworker.js, hosted on the same website as the page using webR.

Here’s what you need to ensure:

1. Worker Scripts: The quarto-webr extension will automatically create and register these workers when your Quarto document is rendered.

2. Directory Structure: Your initial directory structure should include these files:

.
├── _extensions/coatless/webr
└── demo-quarto-webr.qmd

After rendering the Quarto document with the "service-worker" option, your directory will look like this:

.
├── _extensions/coatless/webr
├── demo-quarto-webr.qmd
├── demo-quarto-webr.html  # Rendered document
├── webr-serviceworker.js  # Service workers
└── webr-worker.js

3. Hosting: When hosting your rendered document, you need to make sure the rendered HTML document and the service worker files (webr-serviceworker.js and webr-worker.js) are present on the server. This is important for everything to work correctly:

.
├── demo-quarto-webr.html  # Rendered document
├── webr-serviceworker.js  # Service workers
└── webr-worker.js

If you want to change where the service workers are located, you can set the service-worker-url option in the document YAML. By default, the rendered document will search for the service workers in its current directory.

In a nutshell, the “service-worker” option is a powerful choice for communication with webR, but you need to ensure the correct setup and hosting to make it work smoothly.

Using “post-message” for Communication

Now, let’s delve into the "post-message" option. This sets up a communication channel using something called Post Message. It’s a good choice when you can’t use cross-isolation or service workers. This option is simpler to set up and works in more places, like RStudio’s preview pane or the built-in browser in VSCode.

However, there are some trade-offs. While it’s easier to get started, certain R features that need to pause and wait for input (like readline(), menu(), browser(), etc.) won’t work as expected. Instead, they’ll return empty values like "" or c('', ''). Also, you won’t have the option to stop or interrupt long-running R code. In those cases, you’ll need to refresh the page to regain control.

Feel free to experiment with the "post-message" interface in this webR code cell:

Important

This Quarto extension is open source software and is not affiliated with Posit, Quarto, or webR. The extension is at best a community effort to simplify the integration of webR inside of Quarto generated documents.