Interactivity in charts

Lecture 20

Dr. Benjamin Soltoff

Cornell University
INFO 3312/5312 - Spring 2026

April 9, 2026

Announcements

Announcements

  • Project 02 proposals
  • Homework 05

Viz critique

TODO

Learning objectives

  • Introduce forms of interactivity for data visualizations
  • Identify core interactivity techniques
  • Critique interactivity in data visualizations
  • Implement interactive charts using {ggiraph}

Forms of interactivity

Forms of interactivity

Overview first, zoom and filter, then details on demand

  1. Present the most important figures or most relevant points to the audience
  2. Allow readers to dig into the information, explore, and come up with their own stories

Interactivity techniques

Storytelling form

  • Linear
  • Nonlinear

Interaction techniques

  • Scroll and pan
  • Zoom
  • Open and close
  • Sort and rearrange
  • Search and filter

Examples of interactive graphics

Communicating interactively with data

General approaches

  1. Interactive charts
  2. Scrollytelling
  3. Dashboards
  4. Interactive web applications

Interactive charts with {ggiraph}

Interactive charts with {ggiraph}

  • {ggiraph} makes {ggplot2} graphics interactive
  • Keep familiar {ggplot2} structure while adding hover labels and click behavior
  • girafe() renders an interactive widget from a ggplot object

Basic {ggiraph} example

gapminder_2007 <- filter(
  gapminder,
  year == 2007
) |>
  mutate(
    country_safe = str_replace_all(country, "'", "’")
)

my_plot <- ggplot(
  data = gapminder_2007,
  mapping = aes(
    x = gdpPercap,
    y = lifeExp,
    color = continent,
    tooltip = country_safe,
    data_id = country_safe
  )
) +
  geom_point_interactive() +
  scale_x_log10() +
  theme_minimal()

girafe(ggobj = my_plot)

Basic {ggiraph} example

Custom hover text

my_plot <- ggplot(
  data = gapminder_2007,
  mapping = aes(
    x = gdpPercap,
    y = lifeExp,
    color = continent
  )
) +
  geom_point_interactive(
    mapping = aes(
      tooltip = str_glue("{country_safe}<br>Life expectancy: {round(lifeExp, 1)}"),
      data_id = country_safe
    )
  ) +
  scale_x_log10() +
  theme_minimal()

girafe(
  ggobj = my_plot,
  options = list(
    opts_tooltip(
      use_fill = TRUE,
      css = "background-color: #f8f9fa; color: #111;"
    )
  )
)

Custom hover text

Works with many geoms

bm_hist <- ggplot(
  data = penguins,
  mapping = aes(x = body_mass)
) +
  geom_bar_interactive(
    stat = "bin",
    mapping = aes(
      tooltip = after_stat(count),
      data_id = after_stat(x)
    ),
    color = "white",
    binwidth = 100
  )

girafe(ggobj = bm_hist)

Works with many geoms

Create a (basic) interactive chart with {ggplot2} and {ggiraph}

Get and clean data

library(tidyverse)
library(scales)
library(ggiraph)
library(colorspace)
library(ggbeeswarm)
# colors for the Democratic and Republican parties
dem <- "#00AEF3"
rep <- "#E81B23"

partisan_names <- read_csv(file = "data/partisan-names.csv") |>
  # convert year to a factor column for visualizations
  mutate(
    year = factor(year),
    name_safe = str_replace_all(name, "'", "’")
  )

Static chart

Creating a basic interactive chart

static_plot <- ggplot(
  data = partisan_names,
  mapping = aes(
    x = part_diff,
    y = fct_rev(year),
    color = outcome
  )
) +
  geom_quasirandom() +
  scale_x_continuous(labels = label_percent(style_positive = "plus")) +
  scale_color_manual(values = c(dem, rep), guide = "none") +
  labs(
    title = "The most popular names have gotten more polarized",
    x = "Partisan gap",
    y = NULL,
    caption = '"Blue" and "red" state designations are based on the 2024 presidential election results.\nOnly names assigned at least 100 times in each year were included.\n\nSource: Social Security Administration'
  )
static_plot

Make it interactive

interactive_plot <- ggplot(
  data = partisan_names,
  mapping = aes(
    x = part_diff,
    y = fct_rev(year),
    color = outcome
  )
) +
  geom_point_interactive(
    position = position_quasirandom(),
    mapping = aes(
      tooltip = name_safe,
      data_id = name_safe
    )
  ) +
  scale_x_continuous(labels = label_percent(style_positive = "plus")) +
  scale_color_manual(values = c(dem, rep), guide = "none") +
  labs(
    title = "The most popular names have gotten more polarized",
    x = "Partisan gap",
    y = NULL,
    caption = '"Blue" and "red" state designations are based on the 2024 presidential election results.\nOnly names assigned at least 100 times in each year were included.\n\nSource: Social Security Administration'
  )

girafe(ggobj = interactive_plot)

Make it interactive

Notes on {ggiraph}

  • Uses {htmlwidgets} output, so it works best in HTML formats
  • Interactivity is added explicitly with *_interactive() geoms
  • Three primary aesthetics for interactivity:
    • tooltip: tooltips to be displayed when mouse is over elements
    • data_id: unique identifier for hover and click behavior
    • onclick: JavaScript function to be executed when elements are clicked

Including more information in the tooltip

  • Create a custom tooltip using the format:

    Year: Baby name
    +XX% R/D
  • Generate a new column using mutate() with the required HTML character string

    • str_glue() to combine character strings with data values
    • The <br> is HTML syntax for a line break
    • Use the label_percent() function to format numbers as percents

Create a custom tooltip

partisan_names <- partisan_names |>
  mutate(
    outcome = if_else(outcome == "Trump", "R", "D"),
    abs_part_diff = abs(part_diff),
    pct_label = label_percent(accuracy = 1, style_positive = 'plus')(abs_part_diff),
    fancy_label = str_glue("{year}: {name_safe}<br>{pct_label} {outcome}")
  )
partisan_names |>
  select(year, part_diff, outcome, pct_label, fancy_label)
# A tibble: 200 × 5
   year  part_diff outcome pct_label fancy_label             
   <fct>     <dbl> <chr>   <chr>     <glue>                  
 1 1983      0.884 R       +88%      1983: Kendrick<br>+88% R
 2 1983      0.864 R       +86%      1983: Trey<br>+86% R    
 3 1983      0.854 R       +85%      1983: Rodrick<br>+85% R 
 4 1983      0.833 R       +83%      1983: Ashlea<br>+83% R  
 5 1983      0.780 R       +78%      1983: Tosha<br>+78% R   
 6 1983      0.773 R       +77%      1983: Misti<br>+77% R   
 7 1983      0.767 R       +77%      1983: Latoria<br>+77% R 
 8 1983      0.753 R       +75%      1983: Jackie<br>+75% R  
 9 1983      0.740 R       +74%      1983: Demarcus<br>+74% R
10 1983      0.737 R       +74%      1983: Angelia<br>+74% R 
# ℹ 190 more rows

Create a custom tooltip

static_plot_tooltip_fancy <- ggplot(
  data = partisan_names,
  mapping = aes(
    x = part_diff,
    y = fct_rev(year),
    color = outcome
  )
) +
  geom_point_interactive(
    position = position_quasirandom(),
    mapping = aes(
      tooltip = fancy_label,
      data_id = name_safe
    )
  ) +
  scale_x_continuous(labels = label_percent(style_positive = "plus")) +
  scale_color_manual(values = c(dem, rep), guide = "none") +
  labs(
    title = "The most popular names have gotten more polarized",
    x = "Partisan gap",
    y = NULL,
  ) +
  theme(legend.position = "none")

girafe(
  ggobj = static_plot_tooltip_fancy,
  options = list(
    opts_tooltip(css = "background-color: #ffffff; color: #111111; padding: 6px; border: 1px solid #999;")
  )
)

Create a custom tooltip

Using {ggiraph}

  • Package documentation: {ggiraph} website
  • ggiraph-book for more examples and detailed explanations of {ggiraph} features
  • Most useful helpers: girafe(), opts_tooltip(), opts_hover(), opts_selection()

Application exercise

ae-19

Instructions

  • Go to the course GitHub org and find your ae-19 (repo name will be suffixed with your GitHub name).
  • Clone the repo in Positron, run renv::restore() to install the required packages, open the Quarto document in the repo, and follow along and complete the exercises.
  • Render, commit, and push your edits by the AE deadline – end of the day

Other R packages for interactive charts

Or take INFO 3300/5100 to learn to do this from scratch using D3.js

Wrap-up

Recap

  • Interactivity provides additional dimensions and methods for communicating using data
  • {ggiraph} is a powerful way to create interactive plots in R
  • Use interactive geoms and girafe() to add interactivity to {ggplot2} plots