Clean Room (R)
Working in Python instead? See Clean Room (Python).
Overview
Clean Room turns an R function into a self-contained, reproducible, interactive application. You wrap a function with reproducible(), and GoFigr draws a clean boundary around it: the function can only access the parameters you declare, the packages you list, and publish(). When the function produces a figure, everything—source, data, parameters, environment—is captured and published automatically.
The workflow:
Explore — work however you normally work in RStudio or R Markdown
Distill — pull the core logic into a function with declared parameters
Run — wrap it in
reproducible(); GoFigr captures everythingShare — send a link; stakeholders interact with the figure in the browser
Quick Start
Load the package and configure it:
library(gofigR)
library(ggplot2)
gofigR::enable(workspace_name = "Analytics",
analysis_name = "Penguins")Simplest Example
That's it. When the function runs, GoFigr captures:
Source code — the function body, extracted via R's AST
Parameters — types, defaults, and widget metadata for every argument
Data — data frames passed via
static(), serialized as ParquetEnvironment — package names and versions, R version
Output — the figures published by the run
Interactive Mode
Add interactive = TRUE to launch a Shiny gadget with parameter widgets. Adjusting any control re-runs the function and updates the plot. Click Publish to push a revision with the current parameter values.
Interactive mode requires a live, interactive R session. Inside knitr/R Markdown rendering or non-interactive scripts the gadget is skipped automatically and the function runs once with its default parameter values.
Choosing the viewer
By default the gadget opens in the RStudio Viewer pane (when RStudio is available) or in a dialog window. Override with the viewer argument:
Parameter Widgets
Parameters are declared as function defaults using one of the parameter constructors below. Plain defaults (e.g. bins = 20) are accepted and treated as static values, but for interactive mode you'll want explicit widget constructors.
slider()
slider()Numeric slider for int and numeric values.
Pass an integer literal (20L) to get an integer slider; the gadget preserves integer type when feeding the value back into your function.
dropdown()
dropdown()Categorical dropdown with explicit choices.
checkbox()
checkbox()Boolean toggle.
text_input()
text_input()Free-form text input.
static()
static()Read-only value — no widget is rendered. Use this for data frames and any other value you want captured but not edited interactively. Plain defaults are wrapped in static() automatically, so data = iris and data = static(iris) are equivalent.
In the Clean Room studio the value is available for inspection, and data frames are serialized alongside the revision as Parquet.
Packages
Declare every package the function needs in the packages argument. The clean execution environment will only have access to functions exported by those packages (plus base R and publish()).
Pinning versions
packages accepts a named list to pin explicit versions in the manifest:
If you pass an unnamed character vector, the currently installed versions are resolved automatically.
Imports / aliases
Use the imports argument to record alias-to-package mappings in the manifest (useful for parity with Python clients and for re-creating the environment elsewhere):
Publishing
Call publish() inside the function body. It is always injected into the clean environment — there is no separate "publisher" argument like in Python. The active session set up by gofigR::enable() determines where the figure goes.
In interactive mode publish() is replaced with a no-op preview while you tweak parameters; clicking Publish in the gadget runs the function one more time with the real publish() and shows a link plus QR code to the new revision.
What gets stored
Each published revision includes:
Source code — the function body, extracted from the R AST
Manifest — JSON with parameter types, widget metadata, package versions, and R version
DataFrame parameters — serialized as Parquet
Revision flag — marks the revision as a Clean Room revision
Naming the function
Pass name = "..." to give the clean room function a display name in the webapp. Without it the function is anonymous.
Edge Cases and Caveats
Clean room isolation — The function cannot access variables from your global R environment. Only declared package exports, base R, parameters, and publish() are available. This is by design.
DataFrames are copied — Data frame arguments are round-tripped through Parquet serialization before the function sees them. The function receives a deserialized copy, ensuring the clean room version matches what gets stored.
Other params round-trip through JSON — Atomic parameter values go through jsonlite so the function sees the same values that will be persisted in the manifest.
100 MB limit — Total data frame size (via object.size()) must be under 100 MB. If exceeded, a warning is issued and the function runs normally without clean room metadata.
Unsupported parameter types — Only atomic values (numeric, integer, logical, character), data frames, and nested lists of those types are serializable. Anything else triggers a warning and falls back to direct execution.
nanoparquet required — Clean room support requires the nanoparquet package. Without it a warning is issued and the function runs without clean room metadata.
interactive = TRUE outside an interactive session — Inside knitr or non-interactive R, the gadget is skipped and the function runs once with default parameters.
Source code extraction — The function body is captured from the R AST via body(fn). Functions defined inline as the first argument to reproducible() work out of the box. Programmatically constructed functions also work as long as body(fn) returns a valid expression.
Usage in R Markdown / knitr
reproducible() works inside R Markdown chunks. Interactive mode is automatically downgraded to a single-shot run during knitting:
The chunk renders the figure inline, captures the clean room metadata, and publishes a revision to GoFigr — all from a single function call.
Last updated