--- title: "Pacing and feel: making a recording feel human" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Pacing and feel: making a recording feel human} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", eval = FALSE ) ``` ```{r setup, eval = TRUE} library(vhsR) ``` A `record_demo()` call with default arguments produces a recording that *works* — but it can feel mechanical. Every keystroke is the same length, every line follows the previous with the same beat. Real humans don't type that way, and a good demo doesn't either. This vignette walks through the four arguments that change the feel of the recording without changing the code being demoed. ## The four levers `record_demo()` exposes four pacing controls: - **`typing_speed`** — the delay per character. `"60ms"` is the default; faster (`"40ms"`) is snappier but reads as urgent, slower (`"100ms"`) reads as deliberate. - **`line_pause`** — the sleep after each line, while output renders. Default `"500ms"`. Bump it up for lines whose output you want the viewer to read (`summary()`, `str()`); leave it short for setup lines. - **`paragraph_pause`** — *unset by default*. When set, blank lines in your expression become a sleep of this duration instead of being typed as empty lines. Use it to give viewers a beat between thought units. - **`typing_speed_jitter`** — a number in `[0, 1]`. With jitter, the per-line typing speed is sampled uniformly within `±jitter` of `typing_speed`. The default `0` means every line types at exactly `typing_speed`; `0.3` means each line is somewhere between 70% and 130% of the base speed. Set `set.seed()` first if you want reproducible jitter. These compose. The recipes below show the difference each makes. ## Side by side The same five-line demo, recorded three times with different pacing. ### Mechanical: defaults, no jitter, no paragraph beat ```r record_demo({ fit <- lm(mpg ~ wt, data = mtcars) summary(fit)$coefficients predict(fit, newdata = data.frame(wt = c(2, 3, 4))) }, output = "demo.gif", typing_speed = "60ms") ``` ![Recording with default pacing — uniform typing speed and no paragraph beats.](figures/pacing-mechanical.gif) The blank lines come through as empty `Type ""` directives — the recording moves on the next line immediately, no breathing room. ### Jittered typing speed ```r set.seed(1) record_demo({ fit <- lm(mpg ~ wt, data = mtcars) summary(fit)$coefficients predict(fit, newdata = data.frame(wt = c(2, 3, 4))) }, output = "demo.gif", typing_speed = "60ms", typing_speed_jitter = 0.3) ``` ![Same recording with per-line typing-speed jitter applied — each line types at a slightly different cadence.](figures/pacing-jittered.gif) Each line types at a slightly different cadence. The recording reads less like a TTY playback and more like someone working at the prompt. ### Jittered + paragraph pause ```r set.seed(1) record_demo({ fit <- lm(mpg ~ wt, data = mtcars) summary(fit)$coefficients predict(fit, newdata = data.frame(wt = c(2, 3, 4))) }, output = "demo.gif", typing_speed = "60ms", typing_speed_jitter = 0.3, paragraph_pause = "1s", line_pause = "600ms") ``` ![Recording with jitter and a one-second paragraph pause — blank lines become beats instead of empty Type directives.](figures/pacing-paragraph-pause.gif) The blank lines now produce a one-second beat instead of being typed as empty lines. The viewer has time to actually read the `summary(fit)$coefficients` output before the next block starts. ## A default kit If you don't want to tune from scratch each time, this is a good all-purpose starting point: ```r record_demo( ..., typing_speed = "70ms", typing_speed_jitter = 0.25, line_pause = "600ms", paragraph_pause = "900ms" ) ``` It's slightly slower than the defaults, has gentle per-line variation, and treats blank lines as thought beats. ## When the defaults are wrong - **Dense computational output** (model summaries, large data previews): bump `line_pause` to `"1s"` or `"1.5s"` so viewers can actually read it. - **Short snippet** (3–4 lines, on a tight file-size budget): drop `typing_speed` to `"40ms"` and skip the jitter — speed wins over realism here. - **Walking through a stepwise tutorial**: `paragraph_pause = "1.5s"` and a moderate jitter give viewers time to anticipate each step. ## Reproducibility `typing_speed_jitter > 0` calls `runif()` once per line. Two back-to-back recordings at the same jitter value will *look* slightly different. If your CI workflow re-records as part of release prep and you want bit-stable retries, call `set.seed()` before `record_demo()`. The frame timing inside vhs is still inherently variable (it's a real terminal recording), so "reproducible" here means content-stable, not byte-stable. ## See also - `?record_demo` — full argument reference. - `vignette("recording-for-readme", package = "vhsR")` — where to put the GIF once you've recorded it. - `vignette("tape-scripts", package = "vhsR")` — tape-level pacing for when these args aren't enough.