npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@labcat2020/p5.audioreactive-capture-cli

v0.1.2

Published

Headless capture CLI — Playwright + explicit viewport → per-frame R2 upload (additive over p5.capture.js ZIP fallback)

Readme

@labcat2020/p5.audioreactive-capture-cli

Headless capture CLI for LABCAT audio-reactive sketches — additive over the existing p5.capture.js ZIP fallback.

  • Opens the local/dev sketch URL in headless Chromium (Playwright)
  • Sets explicit viewport for portrait vs landscape
  • Triggers the existing deterministic capture loop (audio seek via _lastPos, MIDI cues, FFT from buffer, draw() per frame @ 60 fps, CSS-background compositing) — no ?exporting=true gate, no Socket.io, no toDataURL over the wire, no frameRate(2) live loop
  • Uploads each frame to R2 as written (or small concurrency batches); no ZIP required for automation. Local toBlob → PNG → R2 (putObject). CSS background is included where sketches use captureCSSBackground / compositeDomCaptureExtension.
  • Local in-browser PNG blobs → ZIP download fallback remains untouched (Chrome multi-download mitigation still applies there).

Borrowed plumbing from Pelletier-Auger p5.js template: headless trigger + off-browser delivery + explicit viewport only. Intentionally not adopting Socket.io/server.js, toDataURL transport, or throttled live-loop export.

Install

From the lab repo (sibling of series repos):

pnpm install
# Playwright browser (once)
npx playwright install chromium

Series repos consume p5.audioreactive-capture@^0.1.3 (adds headless hook) from npm. Local pnpm workspace links when the lab sibling is present.

CLI

labcat-capture --repo <path> --sketch <name> --frames 100 --orient portrait|landscape [options]
# also available as `capture`

| Option | Description | |--------|-------------| | --repo <path> | Path to checked-out series repo (e.g. ../triangles). Required unless --url given | | --sketch <name> | Sketch name or route id (TrianglesNo1, TrianglesNo1.js, number-1). Resolved via src/sketchMetadata.js | | --frames <n> | Frames to capture (default: full audio duration @60fps; test: 100) | | --orient <portrait\|landscape> | Explicit viewport (landscape 1920×1080, portrait 1080×1920). Override with --width/--height | | --width <n> --height <n> | Explicit viewport override | | --r2-prefix <prefix> | R2 key prefix (captures/<sketch>/<orient>/<timestamp> is default; e.g. captures-test/2026-09-06/TrianglesNo1-portrait) | | --r2-bucket <bucket> | R2 bucket (also R2_BUCKET / CAPTURE_R2_BUCKET) | | --r2-account-id <id> | R2 account id (also R2_ACCOUNT_ID / CLOUDFLARE_ACCOUNT_ID) | | --out-dir <dir> | Local output dir (dry-run or mirror) — also enables gallery index.html | | --url <url> | Capture a fully-qualified sketch URL directly (skips repo resolution + dev server) | | --port <n> | Dev server port when auto-spawning (default 4321) | | --no-server | Assume dev server already running | | --dry-run | Force local disk even if R2 creds are set | | --concurrency <n> | Upload concurrency (default 4) | | --verbose | Stream page console.log verbatim |

R2 credentials (when uploading): R2_ACCESS_KEY_ID / CAPTURE_R2_ACCESS_KEY_ID + R2_SECRET_ACCESS_KEY / CAPTURE_R2_SECRET_ACCESS_KEY plus bucket/account above. If absent, CLI falls back to local --out-dir / ./capture-out/....

Workflow (test → approve → full)

# 1) Test ~100 frames to temp R2 (or dry-run if no secrets in CI)
pnpm exec labcat-capture --repo ../triangles --sketch TrianglesNo1 --frames 100 --orient portrait --r2-prefix captures-test/$(date +%F)/TrianglesNo1-portrait
# or dry-run locally:
pnpm exec labcat-capture --repo ../triangles --sketch TrianglesNo1 --frames 100 --orient portrait --dry-run

# 2) Founder reviews (between test and full run) — R2 prefix or simple gallery:
wrangler r2 object list <bucket> --prefix "captures-test/2026-09-06/TrianglesNo1-portrait/"
# if --dry-run: open capture-out/TrianglesNo1-portrait-*/index.html

# 3) Full run after approval (portrait + landscape are separate runs)
pnpm exec labcat-capture --repo ../triangles --sketch TrianglesNo1 --orient portrait --r2-prefix captures/TrianglesNo1
pnpm exec labcat-capture --repo ../triangles --sketch TrianglesNo1 --orient landscape --r2-prefix captures/TrianglesNo1
# frames defaults to full audio length; pass --frames <n> to cap

# 4) Downstream: ffmpeg/DaVinci mux is a follow-up (not this CLI). Pull frames from R2:
# wrangler r2 object get ... or rclone, then ffmpeg -framerate 60 -i 'TrianglesNo1_%05d.png' ...

Bucket / prefix layout

r2://<bucket>/<r2Prefix>/<sketch>/<orient>/<filename>
# default r2Prefix = captures/<sketch>/<orient>/<timestamp>
# example            captures/TrianglesNo1/portrait/20260906-143200/TrianglesNo1_00000.png
  • captures-test/<date>/… for test runs; delete after approval if desired (wrangler r2 object delete or dashboard).
  • Keep portrait and landscape under distinct orient segments so both can be rendered to ProRes later.
  • Local dry-run mirrors the same filename scheme under --out-dir / capture-out/<sketch>-<orient>-<ts>/.

In-browser capture retained

  • Sketches keep initCapture(p, { prefix, enabled, captureCSSBackground, extension }) config-driven; no query-string export mode (?exporting=true) is introduced.
  • Clicking the canvas when captureEnabled (or devtools p.startCapture()) still produces local ZIPs via jszip (<prefix>_frames_part1_<ts>.zip + ffmpeg_command.txt), throttled with MS_BETWEEN_ZIP_DOWNLOADS to avoid Chrome blocking. That path is unchanged and verified as fallback.
  • Headless path sets p.captureSkipDownload = true and uses the additive captureOnFrame / window.__audioReactOnFrameBlob hook (alias __labcatOnFrameBlob kept) to upload per-frame PNGs via toBlob (preferred over toDataURL), preserving CSS-background compositing (compositeDomCaptureExtension / createDomLayerCaptureBackground). If sketch has no initCapture (e.g. TrianglesNo2), CLI auto-injects it via file patch (prefix + composite background) before dev server start and restores after — no manual sketch edit required (disable with --no-auto-init).

Verification

  • CI / secrets-unavailable: labcat-capture --dry-run --frames 5 writes PNGs + gallery to capture-out/ and asserts no ZIP is required; existing ZIP fallback still works in a headed browser.
  • With secrets: short test capture against one sketch (e.g. TrianglesNo1 --frames 100) confirms viewport dimensions, frame count, per-frame R2 keys, and gallery.

Out of scope (intentionally not in this CLI)

  • ?exporting=true query flag
  • Socket.io + local frame server / toDataURL wire
  • Real-time throttled live-loop export (frameRate(2))
  • ffmpeg / DaVinci mux automation (follow-up)
  • Auto Instagram post
  • Removing local ZIP fallback