Skip to contents

Create a gif animation or video from a tmap plot.


  filename = NULL,
  width = NA,
  height = NA,
  dpi = NA,
  delay = 40,
  fps = NA,
  loop = TRUE,
  outer.margins = NA,
  asp = NULL,
  scale = NA,
  restart.delay = NULL,



tmap or a list of tmap objects. If tm is a tmap object, facets should be created, where nrow and ncol in tm_facets have to be set to 1 in order to create one map per frame.


filename. If omitted (default), the animation will be shown in the viewer or browser. If specified, it should be a gif file or a video file (i.e. mp4). The package gifski is required to create a gif animation. The package av (which uses the FFmpeg library) is required for video formats. The mp4 format is recommended but many other video formats are supported, such as wmv, avi, and mkv.

width, height

width and height of the animation file (in pixels). Required when tm is a list, and recommended to specify in advance when tm is a tmap object. If not specified in the latter case, it will be determined by the aspect ratio of the map.


dots per inch. By default 100, but this can be set with the option output.dpi.animation in tmap_options.


delay time between images (in 1/100th of a second). See also fps


frames per second, calculated as 100 / delay. If fps is specified, the delay will be set to 100/fps.


logical that determined whether the animation is looped, or an integer value that determines how many times the animation is looped.


(passed on to tmap_save) overrides the outer.margins argument of tm_layout (unless set to NA)


(passed on to tmap_save) if specified, it overrides the asp argument of tm_layout. Tip: set to 0 if map frame should be placed on the edges of the image.


(passed on to tmap_save) overrides the scale argument of tm_layout (unless set to NA)


not used anymore


arguments passed on to av_encode_video


Not only tmap plots are supported, but any series of R plots.


if (FALSE) {

m1 <- tm_shape(NLD_prov) + 
        tm_polygons("yellow") +
    tm_facets(along = "name")

tmap_animation(m1, delay=40)

data(World, metro)

m2 <- tm_shape(World, projection = "+proj=eck4", simplify = 0.5) +
          tm_fill() +
      tm_shape(metro) + 
          tm_bubbles(size = paste0("pop", seq(1970, 2030, by=10)),
                 col = "purple",
                 border.col = "black", border.alpha = .5,
                 scale = 2) +
      tm_facets(free.scales.symbol.size = FALSE, nrow=1,ncol=1) + 

tmap_animation(m2, delay=100, outer.margins = 0)

m3 <- lapply(seq(50, 85, by = 5), function(age) {
  World$at_most <- World$life_exp <= age
  World_sel <- World[which((World$life_exp <= age) & (World$life_exp > (age - 5))), ]
  tm_shape(World) +
    tm_polygons("at_most", palette = c("gray95", "gold"), = FALSE) +
    tm_shape(World_sel) +
    tm_text("name", size = "AREA", root = 5, remove.overlap = TRUE) +
    tm_layout(main.title = paste0("Life expectency at most ", age), frame = FALSE)

tmap_animation(m3, width = 1200, height = 600, delay = 100)

m4 <- tm_shape(World) +
  tm_polygons() +
tm_shape(metro) +
  tm_bubbles(col = "red") +
  tm_text("name", ymod = -1) +
tm_facets(by = "name", free.coords = F, nrow = 1, ncol = 1) +
  tm_layout( = FALSE, frame = FALSE)

tmap_animation(m4, filename = "World_cities.mp4", 
    width=1200, height = 600, fps = 2, outer.margins = 0)