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

@anthonylzq/node-webcam

v3.0.1

Published

Cross platform webcam capture

Readme

@anthonylzq/node-webcam

Cross platform webcam usage

Install

Requirements

  • Node.js >=20.9.0.

| Platform | Built-in/native path | Optional fallback requirements | Notes | | --- | --- | --- | --- | | Linux | Bundled native V4L2 prebuild for linux-x64 | ffmpeg, then fswebcam | Other Linux architectures/libc variants fall back when no matching native prebuild exists. | | macOS | None yet | ffmpeg, then imagesnap | Camera permissions are controlled by macOS and may require granting access to the terminal/app running Node.js. | | Windows | None yet | ffmpeg with an explicit DirectShow device, then CommandCam.exe via setup | Run npx @anthonylzq/node-webcam setup windows for CommandCam fallback, or set NODE_WEBCAM_COMMANDCAM_PATH. |

Versioning and migration from 2.x

Version 3.0.0 is a semver-major release line. It intentionally does not publish the new capture API as 2.x because several public APIs changed:

| 2.x behavior | 3.x replacement | | --- | --- | | Factory root export | Use create(options) from the package root. | | create(options, type) platform/backend override | Use create(options); backend selection is automatic and capability-aware. | | capture({ type, returnType }) or responseType | Use capture({ location, options }); it always resolves to WebcamCaptureResult. | | Buffer/base64 response modes | Use result.buffer or result.toBase64(). | | list(type) platform override | Use listWebcams({ timeout, signal }) or list() for the current platform. | | Deep imports from dist/* | Import only from @anthonylzq/node-webcam. |

The explicit root-only exports map is part of the 3.x contract. Backend implementations, native bindings, and build output paths are internal and may change without being exposed as supported subpaths.

Linux

Linux uses the bundled native V4L2 addon when a matching prebuild is available, then falls back to ffmpeg when it is installed, and finally to fswebcam. The native backend currently supports single-planar V4L2 streaming devices that accept MJPEG at the requested resolution exactly. Linux camera listing uses the same capability filter when the native addon is available, so multi-planar-only devices are not advertised until the capture path supports them. That filter uses synchronous native V4L2 open()/VIDIOC_QUERYCAP calls, so avoid running Linux camera listing on latency-sensitive request paths if you have unreliable camera drivers.

# Optional fallback backends
# ubuntu

sudo apt-get install ffmpeg fswebcam

# arch
# fswebcam requires a build from the AUR

yay -S ffmpeg fswebcam

Mac OSX

macOS uses ffmpeg when it is installed, then falls back to imagesnap.

# Optional preferred backend
brew install ffmpeg

# Legacy fallback backend
# Repo https://github.com/rharder/imagesnap

brew install imagesnap

Windows

Windows uses ffmpeg when it is installed and a camera device name is provided, then falls back to CommandCam.exe. CommandCam is installed by an explicit setup command so package installation does not depend on lifecycle scripts:

npx @anthonylzq/node-webcam setup windows

The setup command downloads the CommandCam release asset on Windows, verifies its SHA-256 checksum, and stores it in the user-local node-webcam cache. The CommandCam release tag and checksum are pinned in package.json and only need updates when that binary changes. Set NODE_WEBCAM_COMMANDCAM_PATH to use a manually installed executable instead.

On Windows, prefer device names returned by listWebcams() such as "Integrated Webcam". ffmpeg interprets device as a DirectShow device name. CommandCam supports the same friendly names with /devname and also supports 1-based numeric indexes with /devnum. Numeric strings such as "1" are treated as CommandCam indexes and skip ffmpeg selection. CommandCam only supports BMP output; requests for jpeg, jpg, or png require ffmpeg and fail clearly if the fallback would be CommandCam.

Backend selection

capture() and create() choose the first compatible available backend for the current platform. The public API does not require backend selection.

flowchart LR
  A["capture()"] --> B{"Platform"}
  B -->|Linux| C["native:v4l2"]
  C -->|unavailable| D["ffmpeg:v4l2"]
  D -->|unavailable| E["fswebcam"]
  B -->|macOS| F["ffmpeg:avfoundation"]
  F -->|unavailable| G["imagesnap"]
  B -->|Windows| H["ffmpeg:dshow"]
  H -->|unavailable| I["CommandCam"]

| Platform | Selection order | | --- | --- | | Linux | native:v4l2 -> ffmpeg:v4l2 -> fswebcam | | macOS | ffmpeg:avfoundation -> imagesnap | | Windows | ffmpeg:dshow -> CommandCam |

Backend capability checks can change that order:

| Requested options | Eligible backends | | --- | --- | | Basic snapshots (width, height, output, device, timeout, save) | Native and ffmpeg backends are considered first when supported by the platform. | | Legacy capture options (quality, delay, frames, title, subtitle, timestamp, greyScale, rotation, topBanner, bottomBanner, skip) | Legacy backends only: fswebcam, imagesnap, or CommandCam. | | Explicit ffmpegPath on Linux | Skips native Linux and checks ffmpeg first. | | signal cancellation | Uses command backends; native Linux capture is skipped because the addon does not yet accept AbortSignal. | | bmp output | Requires a BMP-capable backend: ffmpeg on Linux/macOS/Windows or CommandCam on Windows. The fswebcam Linux fallback rejects BMP before command execution. |

Selection is re-evaluated for each create() call so camera hotplug, PATH, environment, and permission changes are not hidden by a global cache. ffmpeg availability is checked with ffmpeg -version; it does not open or capture from the camera during selection. The probe has its own short timeout, even when capture timeout is disabled. ffmpeg selection also requires a compatible device signal: Linux devices must exist and pass the V4L2 capture-capability filter when the native probe is available, macOS requires an explicit AVFoundation device, and Windows numeric device strings are reserved for CommandCam. The non-destructive ffmpeg check does not prove that a DirectShow or AVFoundation device name exists; invalid names fail during capture and do not trigger another backend fallback after capture has started. clearBackendCaches() is exported for forwards compatibility and also backs the deprecated clearBackendSelectionCache() alias.

Fallback only happens while selecting a backend. If the selected backend starts a capture and fails because of permissions, an invalid device, a timeout, or an unsupported format, the error is surfaced instead of silently trying the next backend. The backend used for a capture is available as result.backend and result.backendType.

Native webcam performance

The Linux native V4L2 backend avoids spawning a CLI for every capture, so it is usually faster for repeated webcam snapshots. Local benchmark snapshot:

| Backend | 100 captures total | Mean | Median | p95 | Avg bytes | | --- | ---: | ---: | ---: | ---: | ---: | | native:v4l2 | 28.92s | 289.24ms | 287.97ms | 288.72ms | 163,029 | | fswebcam | 36.16s | 361.59ms | 365.52ms | 371.97ms | 96,015 | | ffmpeg:v4l2 | 37.51s | 375.09ms | 370.60ms | 385.05ms | 59,196 |

Relative mean latency, scaled to the slowest backend in this run:

| Backend | Mean latency | Relative bar | | --- | ---: | --- | | native:v4l2 | 289.24ms | ███████████████░░░░░ | | fswebcam | 361.59ms | ███████████████████░ | | ffmpeg:v4l2 | 375.09ms | ████████████████████ |

Benchmark environment: Linux, /dev/video0, 1280x720, 100 captures per backend. Results vary by camera, driver, resolution, codec, CPU, and whether the first capture is warmed up.

Reproduce locally:

npm run benchmark:capture -- --iterations=100 --warmup=3 --backends=native,ffmpeg,fswebcam

The benchmark writes JSON and Markdown reports under tmp/:

tmp/benchmark-results.json
tmp/benchmark-results.md
tmp/benchmarks/<run-id>/results.json
tmp/benchmarks/<run-id>/results.md

Usage

API Usage

All supported public APIs are exported from the package root:

import {
  capture,
  clearBackendCaches,
  create,
  defaults,
  getMetricsReport,
  listWebcams,
  resetMetrics,
  WebcamError,
  type NodeWebcamConfig,
  type WebcamCaptureResult
} from '@anthonylzq/node-webcam'

Deep imports such as @anthonylzq/node-webcam/dist/* are intentionally not supported. The package uses an explicit exports map so internal build layout, native bindings, and backend implementations can change without becoming public API.

  • The simplest use case:

    import { capture } from '@anthonylzq/node-webcam'
    
    const main = async () => {
      const result = await capture({
        location: 'picture.jpeg'
      })
    
      console.log('buffer', result.buffer)
      console.log('base64', result.toBase64())
    }

    On Windows without an eligible ffmpeg device, the CommandCam fallback only supports BMP output. Use a .bmp location there, or install/configure ffmpeg for jpeg, jpg, or png captures.

  • In case you want to use another file type such as jpg, png or bmp you must indicate it in the options object, otherwise you will get an error:

    import { capture } from '@anthonylzq/node-webcam'
    
    const main = async () => {
      const result = await capture({
        location: 'picture.png',
        options: {
          output: 'png'
        }
      })
    
      console.log('buffer', result.buffer)
    }

    This is because in order to build the captured image correctly the file extension and output type must match.

  • If you only need the returned buffer and want to skip file persistence, set save to false:

    import { capture } from '@anthonylzq/node-webcam'
    
    const result = await capture({
      location: 'picture.jpeg',
      options: {
        save: false
      }
    })
    
    console.log(result.buffer)

    You can also provide a custom save handler:

    await capture({
      location: 'picture.jpeg',
      options: {
        save: async (path, buffer) => {
          await uploadSomewhere(path, buffer)
          return false
        }
      }
    })
  • In case you need something more advance you can use the create function that will give you a class that will handle the usage of the webcam for you.

    import { create } from '@anthonylzq/node-webcam'
    
    const webcam = create()
  • In case you want to list the available cameras in your OS, you can use the listWebcams function:

    import { listWebcams } from '@anthonylzq/node-webcam'
    
    const cameras = await listWebcams()
  • The default configuration for all the webcams classes and methods can be found in the defaults object:

    import { defaults } from '@anthonylzq/node-webcam'
    
    console.log(defaults)
    /**
     * {
     *   width: 1280,
     *   height: 720,
     *   quality: 100,
     *   delay: 0,
     *   title: '',
     *   subtitle: '',
     *   timestamp: '',
     *   save: true,
     *   saveShots: true,
     *   output: 'jpeg',
     *   device: '',
     *   verbose: false,
     *   timeout: 0,
     *   frames: 1,
     *   greyScale: false,
     *   rotation: 0,
     *   bottomBanner: false,
     *   topBanner: false,
     *   skip: 0
     * }
     */
  • In case you want aggregate capture metrics, you can use the optional metrics helpers:

    import { getMetricsReport, resetMetrics } from '@anthonylzq/node-webcam'
    
    const metrics = getMetricsReport()
    
    console.log(metrics.captures.total)
    console.log(metrics.captures.averageElapsedMs)
    
    resetMetrics()

Maintainer packaging

Publishing is manual. The only supported release-candidate command is:

npm run publish:check

That command rebuilds the Linux x64 glibc native prebuild, writes prebuilds/native-manifest.json, and runs the package validator. The validator checks that the manifest hashes match the native sources, binding.gyp, and the bundled .node prebuild so stale native artifacts fail before publication. Direct npm pack runs a prepack freshness check, but it does not rebuild the native prebuild; use it only for local inspection, not publication. Never publish a tarball produced with npm pack --ignore-scripts because that skips the freshness checks entirely.

Author

  • Charlie Abeling - Initial Work - Documentation - chuckfairy.

Maintainers

  • Anthony Luzquiños - Rework - Documentation - AnthonyLzq.