Making an Animated Contour Plot
By Jake Thompson in R
September 5, 2018
Earlier this week Mike Bostock tweeted a interesting looking contour plot with a link to edit the formula and manipulate the graphic using D3.js.
A live 2D function plot. Edit the function and reply with interesting images! https://t.co/joWfPgUAAU pic.twitter.com/XYhvJBavRM
— Mike Bostock (@mbostock) September 3, 2018
I decided I would attempt to recreate the image using ggplot2, and animate it using the new gganimate package.
Creating the data
I started by creating a data frame with all the combinations of x
and y
on a grid between -10 and 10, in intervals of 0.1. Then I defined a third variable, z
as a function of x
and y
, using the same equation as originally used
here.
plot_data <- crossing(x = seq(-10, 10, 0.1), y = seq(-10, 10, 0.1)) %>%
mutate(z = sin(sin(x * (sin(y) - cos(x)))) - cos(cos(y * (cos(x) - sin(y)))))
plot_data
#> # A tibble: 40,401 × 3
#> x y z
#> <dbl> <dbl> <dbl>
#> 1 -10 -10 -1.77
#> 2 -10 -9.9 -0.950
#> 3 -10 -9.8 -0.275
#> 4 -10 -9.7 -0.138
#> 5 -10 -9.6 0.0279
#> 6 -10 -9.5 -1.01
#> 7 -10 -9.4 -1.80
#> 8 -10 -9.3 -1.28
#> 9 -10 -9.2 -0.564
#> 10 -10 -9.1 -0.227
#> # … with 40,391 more rows
Now we can use that data to make the image!
Create the plot
To create the plot, we are basically making a heat map of sorts, where the fill is defined by the newly calculated z
variable. I use geom_raster
because it offers speed improvements over geom_tile
when all tiles are the szme size, which they are in this case. Given that we have a lot of tiles, I’ll take the speed!
ggplot(plot_data, aes(x = x, y = y)) +
geom_raster(aes(fill = z), interpolate = TRUE, show.legend = FALSE) +
scale_fill_viridis_c(option = "C") +
coord_equal() +
theme_void()

Pretty good! But then, Jeff Baumes upped the stakes!
A version enabling "t" in the equation that changes over time for animated plots.https://t.co/atNnAXwhFm pic.twitter.com/mEpbJDTQ8N
— Jeff Baumes (@jeffbaumes) September 4, 2018
Well now I can’t not animate it. So…
Add time and animate
To create an animated plot, we need a time variable, t
. We start by creating a data frame similar to the one we created above. It includes all combinations of x
and y
from -10 to 10 in increments of 0.1, but now those values are also cross with the time variable, t
, which goes from 0 to 5 in increments of 0.1. The z
variable, which will still be our fill color, is now a function of x
, y
, and t
, as in
Jeff’s example. Finally, we can use the same ggplot2 code as above, with the addition of the transition_time
function from gganimate.
t_lookup = data_frame(t = c(seq(0, 5, 0.1), seq(4.9, 0, -0.1)),
t2 = seq(0, 10, 0.1))
plot_data <- crossing(x = seq(-10, 10, 0.1), y = seq(-10, 10, 0.1),
t2 = seq(0, 10, 0.1)) %>%
left_join(t_lookup, by = "t2") %>%
mutate(z = sin(sin(x * (sin(y + t) - cos(x - t)))) - cos(cos(y * (cos(x - t) - sin(y + t)))))
ggplot(plot_data, aes(x = x, y = y)) +
geom_raster(aes(fill = z), interpolate = TRUE, show.legend = FALSE) +
scale_fill_viridis_c(option = "C") +
coord_equal() +
theme_void() +
transition_time(t2)

And there we have it! With just one extra variable and one additional line of code for our plot, we have an animated contour plot!