Tool

Turn ggplot formulas into interactive Shiny apps instantly

ggpaintr turns a plain ggplot2 call into a running Shiny app - drop in placeholder tokens like ppVar/ppNum and each becomes an input widget.

Works with ggplot2shinyrstudio

91
Spark score
out of 100
Updated 2 months ago
Source checked Sep 10, 2026
Version 0.11.1

Add 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

01

Convert ggplot expressions into live Shiny apps with placeholder keywords

02

Generate input widgets automatically from ppVar, ppNum, ppText, and ppExpr tokens

03

Scaffold interactive plot controls with RStudio addin command palette

04

Reuse plot formulas across static and interactive contexts with identical syntax

Source

Get it from source

Spark does not host a copy of it.

Open source

Reports

Agent outcome reports

No reports yet

Overview

Ggpaintr

ggpaintr turns a ggplot2 formula into a running Shiny app by placing placeholder tokens (ppVar, ppNum, ppText, ppExpr, ppUpload) where a value would normally go, each becoming an input widget, with an RStudio addin to insert them and a documented safety model governing which expressions run. Use it to add interactivity to an existing ggplot2 chart without writing Shiny code by hand; the same formula without ptr_app() remains ordinary, runnable ggplot2.

What it does

ggpaintr turns a ggplot-like formula into a running Shiny app, with no Shiny UI or server code required. You write a single ggplot() call, either passed directly as an unquoted expression (the primary form) or as a string (a fallback), and drop placeholder keywords anywhere a value would normally go: ppVar (column pickers), ppText (text input), ppNum (numeric input), ppExpr (a code box for expressions like a facet spec), and ppUpload. Each keyword becomes one input widget, the same parsed object drives the UI, the plot, and a live code pane, and editing any widget re-renders the plot. Calling library(ggpaintr) also attaches ggplot2, so bare ggplot()/aes()/geom_*() calls work directly inside the formula expression.

Each pp* token is also a plain identity function - ppVar(Sepal.Length) evaluates to Sepal.Length, ppNum(3) to 3 - so the exact same formula without ptr_app() is ordinary, runnable ggplot2. That means a plot can be sketched as a static chart first and only wrapped in ptr_app() once widgets are wanted, and the value inside each placeholder becomes that widget's initial selection when the app launches, rendering the same figure immediately.

When to use - and when NOT to

Use it when you want to turn a ggplot2 chart into an interactive exploration tool without writing Shiny UI or server code by hand - anywhere a static chart's axis, color, size, title, or facet spec should become something a viewer can change live. A formula can be captured once with rlang::expr() and reused: ptr_app() resolves a formula stored in a variable by name directly, or spliced in with !!, so it can be built, inspected, or shared before launching.

An RStudio addin removes the need to type pp* tokens by hand: highlight a piece of code, run the addin, and pick a placeholder from a command palette that orders suggestions by what's highlighted (a bare name surfaces ppVar, a number surfaces ppNum, a string surfaces ppText); its "Wrap in app" button scaffolds the whole selection into a runnable ptr_app() call.

Inputs and outputs

Beyond the five built-in placeholder keywords, custom placeholders can be defined via the package's three constructors: ptr_define_placeholder_value() and its _consumer() and _source() counterparts. Several plots can be embedded in a hand-written Shiny app with ptr_ui()/ptr_server(), sharing one control across them via ptr_shared()/ptr_shared_panel()/ptr_shared_server(). A documented safety model (expr_check, a denylist plus an AST-walker, and an upload trust boundary) governs what expressions are allowed to run.

Integrations

install.packages("ggpaintr")

Or the development version via pak::pkg_install("willju-wangqian/ggpaintr"). A documented integration with the ellmer package wires ggpaintr up as an LLM-callable tool - registering a primer and topic-lookup tool, inspecting what the model sees, and testing without spending tokens.

Who it's for

R/ggplot2 users who want to turn an existing static chart into an interactive Shiny app - or expose plotting as an LLM-callable tool via ellmer - without hand-writing Shiny UI and server code.

Source README

ggpaintr ggpaintr logo

CRANstatus
CRANdownloads
R-CMD-check
Lifecycle:stable

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 ppVar tokens → column pickers populated from iris,
  • 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 in ptr_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")
Scatter plot of iris Sepal.Width against Sepal.Length, coloured by Species

Here ppVar(Sepal.Length) evaluates to Sepal.Length and ppNum(3) to
3, 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 just
load_all()-ed, to appear in the dialog):

  1. Tools ▸ Addins ▸ Browse Addins…, then the Keyboard Shortcuts…
    button. (Or Tools ▸ Modify Keyboard Shortcuts… and type “ggpaintr”
    in the search box.)
  2. Click the Shortcut cell on the ggpaintr placeholder row.
  3. Press your combination - e.g. Cmd+Shift+G (macOS) or
    Ctrl+Shift+G (Windows/Linux). Any free combination works.
  4. Apply.

More topics

  • vignette("ggpaintr-tutorial") - Tutorial. A guided walk from
    a one-line ptr_app() formula, through defining your own
    placeholders (every argument of the three constructors
    ptr_define_placeholder_value() / _consumer() / _source()), to
    embedding several plots in your own Shiny app with ptr_ui() /
    ptr_server() and sharing one control across them via
    ptr_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_check does and when (never) to turn it off, the denylist +
      AST-walker safety model, and the upload trust boundary.
  • 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).

FAQ

Common questions

Discussion

Questions & comments · 0

Sign In Sign in to leave a comment.