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

timelapse-capture

v0.5.0

Published

Fire-and-forget visual timelapse capture for long-running app review.

Readme

Fire-and-forget visual timelapse capture for long-running app review.

timelapse-capture turns long-running app behavior into short, reviewable videos. Instead of babysitting a dashboard, background job, flaky UI, or multi-hour review session, you point it at a URL, choose a duration and interval, and it captures screenshots in the background. Agents can peek at a single frame while the run is still active, and when it finishes, the tool renders an MP4, keeps useful metadata, and cleans up bulky raw frames by default. It gives engineers, reviewers, and AI agents a lightweight visual audit trail without needing a full test harness or live supervision.

Installation

Requires Node.js 24 or newer and npm. The published package installs the CLI, Playwright, Playwright Chromium, and npm-managed FFmpeg/ffprobe binaries.

Fresh Windows machine

On Windows, use this optional bootstrap only if Node.js/npm are not installed yet. It installs Node.js with winget, then runs the same published npm install and doctor:

$installer = Join-Path $env:TEMP "install-timelapse-capture.ps1"
Invoke-WebRequest -UseBasicParsing https://raw.githubusercontent.com/Open-Agent-Tools/timelapse-capture/main/scripts/install-windows.ps1 -OutFile $installer
powershell -NoProfile -ExecutionPolicy Bypass -File $installer

The script checks for:

  • Node.js 24 or newer (winget install --id OpenJS.NodeJS -e --source winget)
  • the latest timelapse-capture release
  • Playwright Chromium, installed by the package postinstall step
  • FFmpeg and ffprobe, installed as npm package dependencies

If winget is unavailable, install Node.js 24 or newer manually, open a new PowerShell window, then rerun the command.

To install the latest unreleased main build with the same bootstrap path, download the script and pass -Main:

$installer = Join-Path $env:TEMP "install-timelapse-capture.ps1"
Invoke-WebRequest -UseBasicParsing https://raw.githubusercontent.com/Open-Agent-Tools/timelapse-capture/main/scripts/install-windows.ps1 -OutFile $installer
powershell -NoProfile -ExecutionPolicy Bypass -File $installer -Main

Existing Node/npm

npm install -g https://github.com/Open-Agent-Tools/timelapse-capture/releases/latest/download/timelapse-capture.tgz

This installs the CLI globally and automatically installs the Playwright Chromium browser. It also installs npm-managed FFmpeg and ffprobe binaries, so system FFmpeg is not required on PATH.

To install the latest unreleased code from main (useful for testing fixes before a release is cut), use the tarball URL instead of the bare repo URL — recent npm versions have a reify bug that silently fails on npm install -g <git-url>:

npm install -g https://github.com/Open-Agent-Tools/timelapse-capture/tarball/main

The same form (/tarball/<branch-or-commit-sha>) is also the reinstall path; npm install -g over an existing global install updates it in place.

Confirm everything is ready:

timelapse-capture doctor

From source

Clone the repository and link the binary:

git clone https://github.com/Open-Agent-Tools/timelapse-capture.git
cd timelapse-capture
npm install          # also runs postinstall -> playwright install chromium
npm link
timelapse-capture doctor

Doctor

Run doctor before any capture work:

timelapse-capture doctor

The command checks:

  • node: the current Node.js executable satisfies Node.js 24 or newer.
  • playwright: the Playwright package can be imported from this checkout.
  • chromium: Playwright can launch Chromium in headless mode.
  • ffmpeg: the renderer can find and run the npm-managed or system ffmpeg.
  • ffprobe: MP4 validation can find and run the npm-managed or system ffprobe.

Successful output looks like this:

[PASS] node: Node.js 24.1.0 satisfies >= 24.0.0
[PASS] playwright: Playwright package can be imported
[PASS] chromium: Chromium can be launched by Playwright
[PASS] ffmpeg: ffmpeg 7.1 is available
[PASS] ffprobe: ffprobe 7.1 is available
summary: 5 passed, 0 failed, 5 total

If a check fails, read the fix: line, apply it, and run timelapse-capture doctor again before starting a capture.

Use JSON output when another tool needs to parse the result:

timelapse-capture doctor --json

Dogfood Walkthrough

This walkthrough starts from a local web app at http://localhost:3000 and ends with a rendered MP4.

For a full tester protocol with three scenarios (default cleanup, frame retention, and failure modes) and a feedback template, see docs/dogfood-protocol.md.

  1. Confirm dependencies:
timelapse-capture doctor
  1. Start a capture:
timelapse-capture start http://localhost:3000 \
  --duration 30s \
  --interval 5s \
  --viewport 1440x900

The command starts a detached background capture process, prints a run-dir, and returns immediately. Save that path for the next commands. The default location is under ./timelapse-runs/.

start also prints a short alias like cheeky-monkey-427 (deterministically derived from the run directory name). Any command that takes a <run-dir> argument also accepts the alias when the run lives under ./timelapse-runs/. With no argument at all, commands default to the most recently modified run in ./timelapse-runs/.

  1. Check capture progress:
timelapse-capture status ./timelapse-runs/localhost-3000-20260507-121530

Use JSON if you need structured fields:

timelapse-capture status ./timelapse-runs/localhost-3000-20260507-121530 --json
  1. Peek at one frame for inspection:
timelapse-capture peek ./timelapse-runs/localhost-3000-20260507-121530 --latest

peek returns a single image path. Open or inspect that one image; do not load the whole frames/ directory into an agent context.

  1. Wait for the run to finish. render runs automatically when capture completes — poll status until state reads rendered:
timelapse-capture status ./timelapse-runs/localhost-3000-20260507-121530
  1. Inspect the video:
open ./timelapse-runs/localhost-3000-20260507-121530/output.mp4

On Linux, use your desktop file opener or video player instead of open.

Commands

timelapse-capture doctor [--json]

Checks runtime dependencies. Run this first.

npm run check:local

Runs repository checks and tests in sequence. If ffmpeg and ffprobe are not available on PATH, npm run check:local will emit explicit skip messages and still continue to run non-binary tests. test/real-ffmpeg-check.test.mjs is skipped when those binaries are missing.

Quality gates

GitHub Actions runs the same preflight (npm run ci) on every push and pull request to main via .github/workflows/ci.yml. Contributors are still encouraged to run npm run ci locally before pushing — it runs check, format:check, typecheck, and the full Node test suite, so failures show up before CI does.

timelapse-capture start <url>
  [--url <url>] [--duration <2h>] [--interval <5s>] [--video-length <1m>]
  [--fps <24>] [--viewport <1440x900>] [--format <png|jpeg>] [--quality <1-100>]
  [--out <dir>] [--cleanup <mode>]
  [--keep-samples [N]] [--wait-until <event>] [--backend <name>]
  [--json] [--force] [--headed] [--keep-frames] [--keep-latest] [--no-render]
  [--block-websockets]

Starts a detached background process that captures screenshots for the target URL. Durations accept values such as 30s, 5m, 2h, or 500ms. Use --interval <duration> to set capture cadence directly, or use --video-length <duration> with --fps <number> to derive the interval from the requested output video length.

Frames are captured as JPEG by default (--quality 90), since the rendered MP4 is lossy anyway and JPEG frames are roughly 5–10× smaller on disk than PNG. Pass --format png for lossless frames; --quality <1-100> applies only to --format jpeg. The chosen format is recorded in config.json, and latest.* / poster.* artifacts carry the matching extension.

With no --duration, start runs in indefinite mode: it captures until stop is called (or 12 hours elapse, whichever comes first) at a rate that produces ~1 minute of video per hour of capture (2500ms between frames at the default fps=24). --interval and --video-length are not allowed without --duration — omit --duration to get the indefinite defaults.

start writes the initial run artifacts and returns before capture finishes. Use status with the printed run directory to follow progress. The internal child process runs timelapse-capture capture --run <run-dir>.

By default, render runs automatically when capture completes. Pass --no-render to skip auto-render and produce the MP4 manually with render later.

Pass --block-websockets when capturing a live SPA whose own WebSocket feed shares an upstream broadcaster with other clients. A CPU-saturated headless renderer can stop draining inbound WS frames, which back-pressures the upstream sender and stalls its other clients (e.g. real browser tabs talking to the same dashboard bridge). The flag installs a window.WebSocket stub before page scripts run, so the captured page never opens a real socket. The page captures whatever its initial / WS-disconnected state renders to — useful for visual timelapses, unsuitable when the SPA needs live data to render meaningful pixels.

timelapse-capture stop <run-dir> [--json]

Sends SIGTERM to the background capture process. The capture child catches the signal, finishes the current frame, exits the capture loop gracefully, and proceeds to auto-render whatever was captured. Only works on runs in starting or running state. Poll status to observe the run reach rendered.

timelapse-capture status <run-dir> [--json]

Reports run state, captured and failed frame counts, latest successful frame, elapsed time, estimated remaining time, output path, cleanup summary, and disk usage. When a run has completed and auto-render is off (--no-render was passed to start), status prints the render command as a hint.

timelapse-capture peek <run-dir> [--latest | --index <n> | --near <iso>] [--json]

Returns one frame path. --latest selects the newest frame, --index selects a zero-based frame index, and --near selects by ISO 8601 timestamp.

timelapse-capture render <run-dir>
  [--output <file>] [--keep-frames | --keep-samples [N] | --keep-latest | --keep-all]
  [--json] [--force]

Renders output.mp4 from captured frames. Runs automatically when capture completes unless --no-render was passed to start. Call manually when --no-render is in use or to re-render an existing run. By default, successful render removes raw frame PNGs and keeps the MP4 plus run metadata. Pass a retention flag to control what survives the render step.

timelapse-capture cleanup <run-dir> [--keep-frames | --keep-samples | --keep-latest | --frames | --all] [--force]

Deletes raw frame PNGs for a completed run.

  • --keep-frames: preserve all raw frames (no files removed)
  • --keep-samples [N]: remove all but N evenly-distributed representative frames (default: 2, which are first and last)
  • --keep-latest: remove all but the most recent frame
  • --frames: remove raw frames and latest.png, preserve output.mp4 and other artifacts
  • --all: remove the entire run directory; requires --force if raw frames still exist
  • --force: override safety guards (e.g. delete even if frames remain)

Troubleshooting

doctor reports Node.js is too old

Install Node.js 24 or newer, open a new shell, and run:

# Windows
winget install --id OpenJS.NodeJS -e --source winget

node --version
timelapse-capture doctor

Playwright package cannot be imported

Install dependencies from the repository root:

npm install
timelapse-capture doctor

Chromium cannot be launched

Install Chromium for Playwright:

npx playwright install chromium
timelapse-capture doctor

On Linux CI hosts, Playwright may also need OS libraries. Run the command suggested by Playwright if it prints one.

ffmpeg or ffprobe is missing

Reinstall the package so npm restores the managed FFmpeg dependencies, then run doctor again:

npm install -g https://github.com/Open-Agent-Tools/timelapse-capture/releases/latest/download/timelapse-capture.tgz
timelapse-capture doctor

System FFmpeg on PATH is optional. If present, the CLI can use it; otherwise it uses the packaged npm binaries.

Start fails with navigation failed

Check that the URL is complete and reachable from the machine running the command. Use http:// or https://, not a bare host name.

timelapse-capture start http://localhost:3000 --duration 30s

Render fails or no MP4 appears

Run status first and confirm at least one frame was captured. Then check that ffmpeg and ffprobe pass doctor.

timelapse-capture status <run-dir>
timelapse-capture doctor

Render failures preserve raw frames so you can retry after fixing the dependency or input problem.

peek says no frames are available

If render already succeeded, raw frames may have been cleaned up. Inspect poster.png or output.mp4 in the run directory if present.

Retention Examples

Successful render removes raw frame PNGs by default and keeps output.mp4 plus metadata. Use the cleanup command to manually reclaim space if render cleanup was skipped or failed.

# Remove all raw frames (default)
timelapse-capture cleanup ./timelapse-runs/example-com-20260507-121530

# Keep only the first and last frame as samples
timelapse-capture cleanup ./timelapse-runs/example-com-20260507-121530 --keep-samples

# Keep only the most recent frame
timelapse-capture cleanup ./timelapse-runs/example-com-20260507-121530 --keep-latest

# Keep all raw frames
timelapse-capture cleanup ./timelapse-runs/example-com-20260507-121530 --keep-frames

# Remove frames + latest.png, keep output.mp4
timelapse-capture cleanup ./timelapse-runs/example-com-20260507-121530 --frames

# Delete the entire run directory (requires --force if frames exist)
timelapse-capture cleanup ./timelapse-runs/example-com-20260507-121530 --all --force

Artifacts

A run directory contains files like:

timelapse-runs/<slug>-<timestamp>/
  config.json
  job.json
  manifest.json
  manifest.jsonl
  status.json
  latest-frame.json
  latest.png
  capture.log
  render.log
  frames/
    frame-0001.png
    frame-0002.png
  samples/
    sample-000001.png
  output.mp4
  poster.png
  run-summary.json

Important paths:

  • config.json: capture configuration recorded at start (backend, interval, viewport, retention flags).
  • job.json: background process metadata, including the detached child PID and command.
  • manifest.json: start-time run metadata written once when the capture starts (run directory path, creation timestamp, initial state).
  • manifest.jsonl: per-frame capture log. One JSON record per capture attempt (captured, failed, or skipped), matching the schema in docs/PRD.md "Manifest Format".
  • status.json: current or final run status.
  • latest-frame.json: latest captured frame metadata, including path, timestamp, frame index, URL, viewport, and capture status for status and peek.
  • latest.png: copy of the most recently captured frame, updated after each successful capture. Removed by default after render (along with raw frames).
  • capture.log: append-only log of capture lifecycle events from the background capture process.
  • render.log: append-only log of render invocations, including ffmpeg or ffprobe output and exit codes.
  • samples/: retained sample frames copied by render or cleanup when --keep-samples is used, named sample-NNNNNN.png.
  • output.mp4: rendered video.
  • run-summary.json: render and cleanup metadata.
  • poster.png: retained single-frame artifact when render completed after at least one captured frame.