Skip to content

WebP animation support#197

Merged
thomasp85 merged 9 commits intor-lib:mainfrom
klausbrunner:webp-anim
Sep 1, 2025
Merged

WebP animation support#197
thomasp85 merged 9 commits intor-lib:mainfrom
klausbrunner:webp-anim

Conversation

@klausbrunner
Copy link
Contributor

@klausbrunner klausbrunner commented Jul 23, 2025

A sequel to #195 (containing the same changes as it's based on that branch), this adds animation support within a WebP device. Frames are collected in-memory and stitched into an animation on dev.off(). Example:

agg_webp_anim("animation.webp", delay=100)
for(i in 1:10) {
  plot(sin(1:100 + i)) 
  dev.flush()
}
dev.off()

@klausbrunner klausbrunner mentioned this pull request Jul 23, 2025
@klausbrunner klausbrunner marked this pull request as ready for review July 23, 2025 08:03
@klausbrunner
Copy link
Contributor Author

Just to validate this approach, the following little stress test (1000 frames at 4k resolution, lossless) takes about 5 minutes on my laptop and results in a webp file of about 45 MB. Unsurprisingly, CPU is the bottleneck as it maxes out a single core. Memory usage barely changes during the run.

test_that("agg_webp_anim handles large animations", {
  skip_on_cran()

  tmp <- tempfile(fileext = ".webp")
  nframes <- 1000
  agg_webp_anim(tmp, width = 3840, height = 2160, delay = 10, loop = 1)

  colors <- rainbow(nframes)
  cat("Processing", nframes, "frames at 4K resolution...\n")
  start_time <- Sys.time()

  for (i in seq_len(nframes)) {
    if (i %% 100 == 0) cat("Frame", i, "/", nframes, "\n")

    plot.new()
    rect(0, 0, 1, 1, col = paste0(colors[i], "08"), border = NA)
    text(0.5, 0.7, paste("FRAME", i), cex = 6, col = colors[i], font = 2)

    n_points <- 100
    for (j in 1:n_points) {
      x <- j / n_points
      y <- 0.5 + 0.15 * sin(8*pi*x + i/8) * cos(4*pi*x - i/12)
      points(x, y, pch = 19, cex = 3, col = colors[i])
    }

    rect(0.1, 0.1, 0.1 + 0.8 * (i / nframes), 0.15, col = colors[i], border = "black")
    text(0.5, 0.05, paste(i, "of", nframes), cex = 2)

    dev.flush()
  }
  dev.off()

  expect_true(file.exists(tmp))
  expect_gt(file.info(tmp)$size, 100000)

  if (debugging) {
    elapsed <- as.numeric(difftime(Sys.time(), start_time, units = "secs"))
    cat("Big WebP animation:", tmp, round(file.info(tmp)$size / 1024), "KB,", round(elapsed, 1), "seconds\n")
  } else {
    unlink(tmp)
  }
})

@thomasp85 thomasp85 merged commit 1555132 into r-lib:main Sep 1, 2025
13 checks passed
@thomasp85
Copy link
Member

Thanks for all of this

@klausbrunner klausbrunner deleted the webp-anim branch September 1, 2025 21:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants