@maciejwojs/screen-capture
v0.5.2
Published
Native screen capture addon for Node.js
Maintainers
Readme
@maciejwojs/screen-capture
Native Node.js addon for screen capture. Packages are published to NPM with provenance (OIDC) and include prebuilt binaries via GitHub Actions.
Current state
- Windows backend is implemented with
Windows.Graphics.Capture+ D3D11. - Linux backend is implemented with
xdg-desktop-portal(D-Bus) and PipeWire stream, supporting modern desktop environments (Wayland/X11). - On Wayland with NVIDIA, the capture may use MemFd and CPU copy when DMA-BUF zero-copy is unavailable.
- In this MemFd CPU fallback,
getSharedTextureInfo()/ shared texture export may be unavailable andgetPixelData()is the supported path. - Runtime loading uses
node-gyp-build, so local build andprebuilds/binaries are both supported.
Usage
import { ScreenCapture } from '@maciejwojs/screen-capture';
const capture = new ScreenCapture();
capture.start();
// Push-based frame delivery for Node/Electron:
capture.onFrame((frame) => {
console.log('Frame available', frame.backend, frame.width, frame.height);
if (frame.sharedTextureInfo) {
// Use Electron shared texture import path.
} else if (frame.pixelData) {
// Use raw pixel buffer fallback.
}
});
// Get texture structure formatted for Electron's shared-texture API:
const textureInfo = capture.getSharedTextureInfo();
// On Wayland with NVIDIA MemFd CPU fallback, shared texture info may be unavailable.
// Or get the raw handle data (legacy):
const rawHandle = capture.getSharedHandle();
// On Wayland with NVIDIA MemFd, getPixelData() can copy the frame through CPU:
const frameData = capture.getPixelData();
// You can request output format conversion for supported 4-byte layouts:
const rgbaData = capture.getPixelData('rgba'); // only on wayland
capture.stop();Reuse portal session from another addon (Wayland)
If another addon already created and authorized an xdg-desktop-portal session,
you can reuse it and avoid a second permission flow.
From JavaScript always obtain pipewireRemoteFd before portalMonitors (e.g.
do not evaluate inputBridge.getMonitors() before openPipeWireRemoteFd() —
otherwise PipeWire nodes can be placeholders and capture start() may time out):
const capture = new ScreenCapture({
portalSessionHandle: sessionHandleFromInputBridge,
// optional fast-path: skip extra OpenPipeWireRemote call
pipewireRemoteFd: fdFromInputBridge,
// recommended with external fd so monitor list/stream IDs are known immediately
portalMonitors: monitorsFromInputBridge
});When provided external session cannot be used, capture falls back to creating its own session.
When pipewireRemoteFd is set, capture skips portal main-loop flow and starts PipeWire directly.
Install
bun installLocal build
bun run build # to compile TypeScript
bun x node-gyp rebuildPrebuilt binaries (prebuildify)
Build prebuild for current platform:
bun run prebuildifyBuild prebuilds for selected platforms:
bun run prebuildify:allOutput goes to prebuilds/ and is loaded automatically by dist/index.js.
Scripts are configured to run node-gyp via node-gyp internally and build TypeScript beforehand.
How to add other systems / window managers
Recommended backend mapping:
- Windows:
Windows.Graphics.Capture(already in place) - Linux: PipeWire portal (
xdg-desktop-portal) with DMA-BUF limits (already in place) - macOS: ScreenCaptureKit (or CGDisplayStream as fallback)
Practical architecture:
- Keep one JS API (
ScreenCapture) for all OSes. - Keep one addon target in
binding.gyp. - Keep per-platform backend in separate
.cppfiles and select them withbinding.gypconditions. - Current split is:
lib/index.ts- JavaScript export entry point, compiled todist/index.jssrc/addon.cpp- shared N-API wrapper and native binding gluesrc/serialize.cpp- N-API serialization for Electron shared textures and raw handlessrc/win/capture_winrt.cpp- Windows WinRT capture backendsrc/win/capture_dxgi.cpp- Windows DXGI capture backendsrc/win/capture_gdi.cpp- Windows GDI capture backendsrc/win/capture_factory.cpp- Windows backend selection and helper logicsrc/linux/platform_capture_linux.cpp- Linux portal / PipeWire implementationsrc/pixel_conversion.cpp- color conversion helper for supported pixel layoutssrc/platform_capture_stub.cpp- compile-time fallback for unsupported systemssrc/platform_capture.hpp- common backend interface and shared definitions
binding.gypalso supportsforce_apibuild flags (winrt,dxgi,gdi) for Windows variants.- For unsupported combinations, the stub backend returns a clear runtime error.
To add a new platform, implement the backend in a new source file (for example src/macos/platform_capture_macos.cpp) and add it to the matching binding.gyp condition branch.
This keeps one npm package with both local build support and published prebuilds/ binaries, without requiring users to compile manually.
