The single most common reason to reach for vhsR is to ship an
animated demo at the top of a package’s README. This vignette walks
through the canonical workflow — from a fresh record_demo()
call to a GIF that GitHub renders inline.
Then in your README.md (or README.Rmd):
That’s it. The recording runs once on your machine, gets committed to
the repository, and renders inline on GitHub, pkgdown, and CRAN’s HTML
package page (where supported). Treat the GIF as you would any other
artefact under man/figures/ — refresh it when the demo’s
content changes, otherwise leave it alone.
README.Rmd and the vhsr knitr
engineIf you maintain a README.Rmd, vhsR registers a
vhsr knitr engine on package load that records and
image-includes in one step. Inside README.Rmd:
``` vhsr
x <- 1:5
mean(x)
head(cars, 3)
```
When you run devtools::build_readme() (or knit by hand),
the chunk:
output=;README.md.Forwarded chunk options are output,
backend, and any pacing or styling argument accepted by
tape_options() (e.g. width,
height, font_size, theme,
typing_speed, line_pause,
start_pause, paragraph_pause). See
?tape_options for the full list. Anything else is
ignored.
Multi-line |> pipelines record verbatim — the
recording shows the pipe form you typed, not the desugared nested-call
form R’s parser produces internally:
record_demo({
library(dplyr)
mtcars |>
filter(cyl == 6) |>
arrange(desc(mpg))
}, output = "man/figures/demo.gif")Same goes for blank lines: an empty line between two statements stays
empty in the recording, which composes nicely with
paragraph_pause when you want a beat between sections.
man/figures/ is the convention. roxygen, pkgdown, and
CRAN’s package page all know to look there. Commit the recorded GIF:
record_demo() (or rebuilding the README) and committing the
new GIF.A common make target keeps the workflow explicit:
demo: man/figures/demo.gif
man/figures/demo.gif: scripts/demo.R
R -e 'vhsR::record_demo_file("scripts/demo.R", output = "$@")'
GitHub renders inline GIFs up to about 10 MB but anything over 1–2 MB starts to feel slow on a slow connection. To stay small:
width = 1000, height = 500 is a good balance.typing_speed = "40ms"
(instead of the "60ms" default) shaves a meaningful amount
of frames.If the GIF is still too big, switch to .mp4 or
.webm:
GitHub’s README renderer doesn’t autoplay video, but pkgdown and any
HTML render do. Embed it with vhsr_widget():
…which produces a
<video controls loop muted playsinline> tag tree
suitable for a chunk with results = "asis".
vhsr_install()
auto-downloads vhs on macOS but prints
brew install lines for ttyd and
ffmpeg (those have no usable upstream binaries on macOS).
The recorder doesn’t care whether the binaries came from brew or the
cache.man/figures/. Files
there are only included in the built tarball if they’re referenced from
a help page (\figure{...} in an .Rd). For a
README image this almost never matters — the README on GitHub references
the same file directly — but be aware if you also reference it from
package documentation.devtools::check() re-record the README at check time. The
eval = FALSE chunks in this vignette, combined with
committed figures, are the safe pattern.vignette("pacing-and-feel", package = "vhsR") — making
a recording feel less robotic.vignette("tape-scripts", package = "vhsR") — when you
outgrow record_demo().?record_demo, ?record_demo_file,
?vhsr_widget.