Skip to contents

gganime takes a ggplot2 plot written with gganimate syntax and renders it as an animated SVG. You build the plot the same way you would for gganimate, then call anime() in place of animate(). The output is a self-contained HTML widget: one vector drawing of the scene with a timeline over its elements, played in the browser.

gganime reuses gganimate’s build machinery unchanged and only replaces the final rendering step, so the gganimate documentation remains the reference for the grammar itself.

The animations below are live. Drag the scrub bar, or use the play and pause control, to step through a frame at a time.

Your first animation

Start from a static plot.

library(ggplot2)
library(gganime)

p <- ggplot(iris, aes(Petal.Width, Petal.Length)) +
  geom_point()

Add a transition to animate it. transition_states() splits the data by a discrete variable and moves the points between the resulting states.

anim <- p +
  transition_states(Species, transition_length = 2, state_length = 1)

To see the animation with gganimate you would print the object or call animate(). With gganime you call anime():

anime(anim, nframes = 30)

Transitions

gganime supports three of gganimate’s transitions.

transition_states(), above, animates between the levels of a discrete variable.

transition_time() animates along a continuous variable, holding it to the data range so the spacing between frames matches the spacing between values.

aq <- ggplot(airquality, aes(Day, Temp)) +
  geom_point() +
  transition_time(Month)

anime(aq, nframes = 30)

transition_reveal() keeps earlier data on screen and reveals the rest along a dimension, which suits a line growing over time.

ec <- ggplot(economics, aes(date, unemploy)) +
  geom_line() +
  transition_reveal(date)

anime(ec, nframes = 40)

Frame labels

A transition exposes a set of per-frame variables. Insert them into the title, subtitle, or caption with glue syntax, and the text is swapped frame by frame in step with the geometry.

aq_labelled <- ggplot(airquality, aes(Day, Temp)) +
  geom_point() +
  transition_time(Month) +
  labs(title = "Month: {frame_time}")

anime(aq_labelled, nframes = 30)

Each transition provides different variables. transition_states() gives closest_state; transition_time() gives frame_time; transition_reveal() gives frame_along. All three also provide frame, nframes, and progress.

A label may span several lines. Each line is swapped on its own, so a static first line can sit above a changing second one:

aq_two_line <- ggplot(airquality, aes(Day, Temp)) +
  geom_point() +
  transition_time(Month) +
  labs(title = "Temperature by day\nMonth: {frame_time}")

anime(aq_two_line, nframes = 30)

The number of lines has to be the same in every frame. The text layout is taken from the first frame, so a label that gains or loses a line partway through is held at its first-frame text with a warning.

Easing, entering, and exiting

ease_aes() sets how an aesthetic moves between values across a transition. The default is linear; naming an aesthetic gives it its own easing.

anime(anim + ease_aes("cubic-in-out"), nframes = 30)

enter_*() and exit_*() control how data that appears or leaves is drawn, so its entrance and exit can be animated.

anim_species <- ggplot(iris, aes(Petal.Width, Petal.Length)) +
  geom_point(aes(colour = Species), size = 2) +
  transition_states(Species, transition_length = 2, state_length = 1)

anime(anim_species + enter_fade() + exit_shrink(), nframes = 30)

Each family animates a different attribute: fade the paint opacity, grow and shrink the size, drift, fly and manual the position, recolour the paint. All of them work with the four supported geoms.

Two behaviours below come from gganimate itself and appear in its gifs as well. enter_recolour() and exit_recolour() set colour and fill together, and setting the aesthetic a geom does not paint makes tweenr coerce a logical column, which warns; pass NA for that aesthetic, as in enter_recolour(colour = "white", fill = NA) for the default point shape. enter_grow() and exit_shrink() do not change the geometry of a geom_ribbon() or geom_area() layer, so a ribbon that arrives is drawn at its full shape.

Several layers at once

Layers are animated independently, so you can stack them the way you would in a static plot. Each layer keeps its own elements even when two layers draw the same kind of shape.

aq_layers <- ggplot(
  airquality[airquality$Month %in% c(5, 6, 7), ],
  aes(Day, Temp)
) +
  geom_area(fill = "#9ecae1") +
  geom_line(linewidth = 1, colour = "#08519c") +
  geom_point(size = 2, colour = "#a50f15") +
  labs(title = "Month {closest_state}") +
  transition_states(Month, transition_length = 2, state_length = 1)

anime(aq_layers, nframes = 30)

A layer that does not use the transition variable is drawn once and left in place, which is how you add a fixed reference against the animated data.

Leaving a trail

shadow_mark() keeps the marks from earlier frames on screen behind the current one, so a scatter builds up as it advances.

aq_shadow <- ggplot(airquality, aes(Day, Temp)) +
  geom_point() +
  transition_time(Month) +
  shadow_mark(colour = "grey70") +
  labs(title = "Month: {frame_time}")

anime(aq_shadow, nframes = 30)

shadow_trail() keeps every nth frame instead, spacing the marks out in time rather than showing all of them. distance sets the spacing as a fraction of the animation, and max_frames caps how many marks are kept, so the oldest one drops away as a new one is added.

aq_trail <- ggplot(airquality, aes(Day, Temp)) +
  geom_point() +
  transition_time(Month) +
  shadow_trail(distance = 0.1, max_frames = 5, colour = "grey70") +
  labs(title = "Month: {frame_time}")

anime(aq_trail, nframes = 30)

Controlling the render

gganime fixes the frame count and playback speed when you call anime(), the same way gganimate does at animate():

  • nframes sets how many frames the animation is sampled at (default 100).
  • fps sets the playback speed in frames per second (default 10), or give duration in seconds to set the speed from the total length.
  • loop repeats the animation: TRUE, FALSE, or a number of iterations.
  • controls shows or hides the play and scrub bar.
  • width and height fix the widget size in pixels; leaving them NULL lets it fill its container.
  • precision rounds the animated coordinates, which trims the file size.
anime(anim, nframes = 50, duration = 4, loop = FALSE)

Saving and embedding

anime() returns an htmlwidget, so it prints in the RStudio Viewer, knits into R Markdown and Quarto documents, and saves to a standalone HTML file with htmlwidgets::saveWidget():

htmlwidgets::saveWidget(anime(anim), "iris.html")

Loading gganime also makes a bare gganimate plot print through anime() at the console, so printing anim gives the widget without an explicit call. Set options(gganime.autoprint = FALSE) to keep gganimate’s own output.

In Shiny, pair renderGganime() in the server with gganimeOutput() in the UI.

Supported features

Anything else stops with a message naming an alternative. Current limits include non-linear coordinate systems (coord_polar(), coord_radial(), coord_transform(), coord_sf()), shadow_wake(), and view_*() other than view_static().