Skip to content

demo: scripted, reproducible README screencast tooling - #76

Merged
martinus merged 7 commits into
masterfrom
demo-gif
Jul 19, 2026
Merged

demo: scripted, reproducible README screencast tooling#76
martinus merged 7 commits into
masterfrom
demo-gif

Conversation

@martinus

Copy link
Copy Markdown
Owner

Adds scripts/demo/ — a one-command, reproducible way to record the oans demo GIF for the README. No manual typing, fully re-runnable.

Usage

mkdir -p ~/oans-demo && cd ~/oans-demo
/path/to/oans/scripts/demo/record.sh   # → ~/oans-demo/demo.gif

What's here

  • setup.sh — builds the demo tree on btrfs/xfs. Deliberately shaped so the scan/hashing phase is the long, visible one and the dedupe phase is short: ~2700 unique files (hashed, never deduped) + 100 small duplicated groups. File sizes follow an exponential distribution (many small, a few large) from a fixed seed. Copies use --reflink=never (on btrfs cp reflink-copies by default, which would leave nothing to reclaim). Defaults ≈ 3000 files / ~6 GiB / 100 dedupe groups; every knob is env-overridable (DEMO_UNIQUE, DEMO_DUP_GROUPS, DEMO_COPIES, DEMO_MEAN_KB, …).
  • record.sh — builds oans, generates the dataset in the current directory (so --stats/--history show short paths), records with VHS if installed (highest quality) else asciinema + agg, then gifsicle-optimizes the GIF (~30% smaller, quality-neutral). Cleans up the dataset afterward, exposes oans via PATH (never copies/deletes your binary).
  • demo.tape — the VHS screenplay (Dracula theme, Hack font, 1280×760; Wait+Screen /Reclaimed/ so timing is correct regardless of dataset size).
  • play.sh — the same screenplay for the asciinema+agg fallback.

Notes

  • Verified end-to-end locally (VHS path): correct dataset shape, hash-vs-dedupe split, single optimized GIF output.
  • Format choice: for terminal casts an optimized GIF is the smallest inline asset — animated WebP doesn't beat it and MP4 can't embed in plain markdown — so the script emits just the GIF.
  • Not wired into the README yet — that's a follow-up once a rendered assets/demo.gif is committed.

🤖 Generated with Claude Code

martinus and others added 5 commits July 19, 2026 12:48
Add scripts/demo/ to record the oans demo GIF in one command, deterministically:

- setup.sh — builds a demo tree on btrfs/xfs: mostly unique files (hashed, long
  scan phase) plus a small set of duplicated groups (short dedupe phase), with
  exponential file sizes (many small, few large) from a fixed seed. Copies use
  --reflink=never so oans actually has something to reclaim. Defaults: ~3000
  files, ~6 GiB, 100 dedupe groups. All knobs are env-overridable.
- record.sh — one entrypoint: builds oans, generates the dataset in the current
  directory (short paths in --stats/--history), records with VHS if installed
  else asciinema+agg, then gifsicle-optimizes the GIF. Cleans up the dataset.
- demo.tape — the VHS screenplay (theme, font, timing; waits for the summary).
- play.sh — the same screenplay for the asciinema+agg fallback.

Not wired into the README yet — that follows once a render is committed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Generating tens of thousands of files with one head/cp per file was slow. Now:
- one awk pass plans every path + exponential size (no per-file shell subshells),
- a pool of python workers (one per core) each open /dev/urandom once and stream
  bytes for many files — no per-file process spawns,
- copies are made with a single parallel `cp` via xargs -P.
Measured ~0.26s for 3150 files (32 cores) vs seconds of spawns before.

Also drop the page cache after generation (best-effort sudo; DROP_CACHES=0 to
skip) so the recorded scan reads cold from disk — realistic, and it makes the
hashing phase visibly longer.

Verified: correct file counts and no accidental dedupe of the unique files
(oans dedupes exactly DUP_GROUPS groups).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the inline python heredoc in setup.sh with a real, lintable
scripts/demo/gen.py, and fill files with Python's random (Mersenne Twister)
instead of /dev/urandom — plenty for a demo (bytes only need to be distinct and
incompressible). Each parallel worker seeds its RNG from its chunk filename, so
the streams are independent and two "unique" files can't collide (verified: oans
still dedupes exactly DUP_GROUPS groups). ~0.32s for 3150 files.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The tape waited for /Reclaimed/, but the dedupe summary's headline wording varies
by oans version (e.g. "Deduplicated ... / Kernel scanned ..."), so on some builds
the match never fired and VHS hung until the 300s timeout. Wait for "Elapsed"
instead — printed on every dedupe run regardless of wording — and bump the cap to
600s for very large trees. Verified end-to-end: render completes, no timeout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Terminal casts read fine at 12fps, and halving the framerate roughly halves the
frame count — the main GIF-size driver, since the scan progress bar changes every
frame. Also expose the gifsicle --lossy level as DEMO_LOSSY (default 60, was 30).
Measured ~25% smaller on a warm run; more on a long cold scan where the changing
frames dominate. Text stays crisp.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
martinus and others added 2 commits July 19, 2026 13:48
Commit the generated demo GIF (assets/demo.gif) and embed it as the hero image
near the top of the README, sized to 900px so it maps 1:1 to GitHub's content
column.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@martinus
martinus merged commit 953f014 into master Jul 19, 2026
2 checks passed
@martinus
martinus deleted the demo-gif branch July 19, 2026 11:51
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.

1 participant