Turn ggplot formulas into interactive Shiny apps instantly
ggpaintr turns a single ggplot() call into a live Shiny app by dropping placeholder tokens like ppVar and ppNum into your plot code.
Maintainer of this project? Claim this page to edit the listing.
0.11.1Add to Favorites
Why it matters
Transform static ggplot2 visualizations into interactive Shiny applications without writing UI or server code, enabling rapid prototyping and deployment of data exploration tools.
Outcomes
What it gets done
Convert ggplot expressions into live Shiny apps with placeholder keywords
Generate input widgets automatically from ppVar, ppNum, ppText, and ppExpr tokens
Scaffold interactive plot controls with RStudio addin command palette
Reuse plot formulas across static and interactive contexts with identical syntax
Install
Add it to your toolbox
Run in your project directory:
curl -fsSL https://spark.entire.vc/get/willju-wangqian-ggpaintr | bash Overview
Ggpaintr
ggpaintr converts a single ggplot2 call into a running interactive Shiny app by replacing values with placeholder tokens (ppVar, ppNum, ppText, ppExpr, ppUpload) that become input widgets. Editing any widget re-renders the plot and an accompanying live code pane, with no separate Shiny UI or server code required. Use it when you already have a working ggplot2 call and want an interactive version without writing Shiny code; skip it if you need a full custom Shiny app beyond swapping ggplot2 arguments.
What it does
ggpaintr turns a single ggplot2 call into a running Shiny app. You write one ggplot() call - either passed directly as an unquoted expression (the primary form) or as a string (the fallback) - and drop placeholder keywords (ppVar, ppText, ppNum, ppExpr, ppUpload) anywhere a value would normally go. Each keyword becomes an input widget: the same parsed object drives the UI, the plot itself, and a live code pane, so editing any widget re-renders the plot. No separate Shiny UI or server code is required - if you can write a ggplot call, you can ship an interactive version of it.
Because each pp* token is a plain identity function, the exact same formula without ptr_app() is ordinary, runnable ggplot2: you can drop the placeholders into a plot, sketch it as a static chart first, and only wrap it in ptr_app() when you want the widgets. When ptr_app() is called, the value inside each placeholder becomes that widget's initial selection, so the app boots pre-populated and renders the same figure immediately, ready to be re-pointed at other columns.
ggpaintr also ships an RStudio addin that turns a plain ggplot expression into a placeholder formula without typing the pp* tokens by hand. Highlighting a piece of code and running the addin opens a command palette to pick a placeholder - the selection is rewritten in place (mpg becomes ppVar(mpg); nothing highlighted inserts ppVar() with the cursor between the parens). The palette list is ordered by what was highlighted: a bare name surfaces the column pickers (ppVar), a number offers ppNum, a string offers ppText, and a call offers the layer/verb toggles (ppLayerOff / ppVerbSwitch). The palette's "Wrap in app" button wraps the whole selection in { ... } |> ptr_app() to scaffold a runnable app.
When to use - and when NOT to
Use ggpaintr when you already know how to write a ggplot2 call and want an interactive version of that exact plot - swappable columns, adjustable numeric parameters, editable text and facet expressions - without hand-writing Shiny UI or server code. It fits prototyping, exploratory data analysis, and quickly turning a static chart into something a collaborator can re-point at other columns.
It is not a general Shiny app builder: ptr_app() needs a live Shiny session to run, so it is meant for interactive use, not static rendering pipelines or batch report generation. If a plot doesn't need to be interactive, or the interactivity needs go well beyond swapping ggplot2 arguments (custom layouts, multi-page apps, non-plot widgets), plain Shiny or another framework is the better fit.
Inputs and outputs
Input is a single ggplot2 expression containing placeholder keywords - passed as an unquoted expression (the primary form) or as a string (the fallback, useful when the formula is built or fetched as text). The five built-in placeholders are ppVar (column picker), ppNum (numeric input), ppText (text input), ppExpr (code box, e.g. for a facet spec like vars(Species)), and ppUpload. A formula can also be captured once with rlang::expr() and reused across calls, or resolved by variable name inside ptr_app() without needing !! splicing.
Output is a running Shiny app: each placeholder token renders as its corresponding widget, and the app's plot and an accompanying live code pane both update as widgets are changed. Custom placeholders can be registered in the session using the ptr_define_placeholder_value(), ptr_define_placeholder_consumer(), and ptr_define_placeholder_source() constructors, and multiple plots can be embedded in a larger Shiny app and share one control via ptr_ui() / ptr_server() and ptr_shared() / ptr_shared_panel() / ptr_shared_server().
Integrations
ggpaintr builds directly on ggplot2 - library(ggpaintr) attaches ggplot2 automatically, so bare ggplot() / aes() / geom_*() calls work directly inside the formula expression. It integrates with rlang for expression capture and splicing (rlang::expr(), !!). It also integrates with RStudio through its addin, and can be wired up as an LLM-callable tool using the ellmer package, registering a primer and a topic-lookup tool so an LLM can call ggpaintr functions directly.
pak::pkg_install("willju-wangqian/ggpaintr")
Who it's for
R users and data analysts who already write ggplot2 code and want to turn a chart into an interactive tool for themselves or collaborators without learning Shiny's UI/server model. It also suits R package or app authors who want to expose plotting functionality to an LLM via ellmer, or who want a fast way to prototype an interactive dashboard from existing plotting code.
Source README
ggpaintr 
Overview
ggpaintr turns a ggplot-like formula into a running Shiny app. You write
a single ggplot() call - passed directly as an unquoted expression
(the primary form), or as a string (the fallback) - and drop placeholder
keywords (ppVar, ppText, ppNum, ppExpr, ppUpload) anywhere a
value would normally go. ggpaintr does the rest: each keyword becomes an
input widget, the same parsed object drives the UI, the plot, and a live
code pane, and editing any widget re-renders the plot.
No Shiny UI or server code required. If you can write a ggplot call, you
can ship an interactive version of it.
Installation
# Install the released version from CRAN:
install.packages("ggpaintr")
# Or the development version from GitHub:
# install.packages("pak")
pak::pkg_install("willju-wangqian/ggpaintr")
Usage
library(ggpaintr)
# Primary form: pass the ggplot() call directly as an unquoted expression.
ptr_app(
ggplot(data = iris, aes(x = ppVar, y = ppVar)) +
geom_point(aes(color = ppVar), size = ppNum) +
labs(title = ppText) +
facet_wrap(ppExpr)
)
That single call returns a running Shiny app. Each placeholder in the
formula becomes one widget:
- the three
ppVartokens → column pickers populated fromiris, ppNum→ a numeric input (point size),ppText→ a text input (plot title),ppExpr→ a code box for the facet spec (e.g.vars(Species)).
library(ggpaintr) also attaches ggplot2, so bare ggplot() /aes() / geom_*() calls work directly in the formula expression. To
reuse a formula across calls, store it with rlang::expr() and splice
it in with !! - f <- rlang::expr(ggplot(...)); ptr_app(!!f). The
string form (ptr_app("ggplot(...)")) remains a supported fallback,
handy when a formula is built or fetched as text. To swap in a custom
page shell or theme, write a thin wrapper on top of the public
primitives.
The ptr_app() call above needs a Shiny session to run, so it is not
evaluated here. But each pp* token is a plain identity function, so
the same formula without ptr_app() is ordinary, runnable ggplot2
- drop the placeholders into your plot, sketch it as a static chart
first, then wrap it inptr_app()when you want the widgets:
ggplot(iris, aes(x = ppVar(Sepal.Length), y = ppVar(Sepal.Width), color = ppVar(Species))) +
geom_point(size = ppNum(3)) +
labs(x = "Sepal.Length", y = "Sepal.Width", color = "Species")
Here ppVar(Sepal.Length) evaluates to Sepal.Length and ppNum(3) to3, so the chart renders exactly as plain ggplot2 - the tokens only
become widgets once the expression is handed to ptr_app().
Handing that exact expression to ptr_app() launches the interactive
app, and the value inside each placeholder becomes that widget’s
initial selection - so the app boots with x = Sepal.Length,y = Sepal.Width, color = Species, size = 3 pre-selected and
renders the very same figure straight away, ready to be re-pointed at
other columns:
ptr_app(
ggplot(iris, aes(x = ppVar(Sepal.Length),
y = ppVar(Sepal.Width),
color = ppVar(Species))) +
geom_point(size = ppNum(3))
)
The expression need not be inline. Capture it once with rlang::expr()
and reuse it - ptr_app() resolves a formula stored in a variable by
name (no !! needed), so you can build, inspect, or share the formula
before launching the app:
formula_iris <- rlang::expr(
ggplot(iris, aes(x = ppVar(Sepal.Length),
y = ppVar(Sepal.Width),
color = ppVar(Species))) +
geom_point(size = ppNum(3))
)
ptr_app(formula_iris)
RStudio addin
ggpaintr ships an RStudio addin that turns a plain ggplot expression
into a placeholder formula without typing the pp* tokens by hand.
Highlight a piece of your code, run the addin, and pick a placeholder
from a command palette - the selection is rewritten in place (mpg →ppVar(mpg); nothing highlighted inserts ppVar() with the cursor
between the parens). The list is ordered by what you highlighted - a
bare name surfaces the column pickers (ppVar), a number ppNum, a
string ppText, a call the layer/verb toggles (ppLayerOff /ppVerbSwitch) - and any placeholder you registered this session
appears automatically. The palette’s Wrap in app button wraps the
whole selection in { … } |> ptr_app() to scaffold a runnable app.
Run it from the Addins menu ▸ ggpaintr placeholder. To bind a
keyboard shortcut (the addin must be installed, not justload_all()-ed, to appear in the dialog):
- Tools ▸ Addins ▸ Browse Addins…, then the Keyboard Shortcuts…
button. (Or Tools ▸ Modify Keyboard Shortcuts… and type “ggpaintr”
in the search box.) - Click the Shortcut cell on the ggpaintr placeholder row.
- Press your combination - e.g. Cmd+Shift+G (macOS) or
Ctrl+Shift+G (Windows/Linux). Any free combination works. - Apply.
More topics
vignette("ggpaintr-tutorial")- Tutorial. A guided walk from
a one-lineptr_app()formula, through defining your own
placeholders (every argument of the three constructorsptr_define_placeholder_value()/_consumer()/_source()), to
embedding several plots in your own Shiny app withptr_ui()/ptr_server()and sharing one control across them viaptr_shared()/ptr_shared_panel()/ptr_shared_server(). Opens
with a tour of the five built-in placeholder keywords.**Safety
- chapter of the ggpaintr book (development-version docs).** What
expr_checkdoes and when (never) to turn it off, the denylist +
AST-walker safety model, and the upload trust boundary.
- chapter of the ggpaintr book (development-version docs).** What
Using ggpaintr from an LLM with
ellmer -
chapter of the ggpaintr book (development-version docs). Wire
ggpaintr up as an LLM-callable tool with ellmer: register the primer
and the topic-lookup tool, inspect what the model sees, swap
providers/models, and test without spending tokens.
The pkgdown reference
lists every exported function. The ggpaintr
book is the longer,
book-length introduction (documents the development version).
Discussion
Questions & comments · 0
Sign In Sign in to leave a comment.