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 🙏

© 2025 – Pkg Stats / Ryan Hefner

node-mac-recorder

v2.22.0

Published

Native macOS screen recording package for Node.js applications

Readme

node-mac-recorder

This package was developed for https://creavit.studio

A powerful native macOS screen recording Node.js package with advanced window selection, multi-display support, and automatic overlay window exclusion. Built with ScreenCaptureKit for modern macOS with intelligent window filtering and Electron compatibility.

Features

Advanced Recording Capabilities

  • 🖥️ Full Screen Recording - Capture entire displays with ScreenCaptureKit
  • 🪟 Window-Specific Recording - Record individual application windows
  • 🎯 Area Selection - Record custom screen regions
  • 🖱️ Multi-Display Support - Automatic display detection and selection
  • 🎨 Cursor Control - Toggle cursor visibility in recordings
  • 🖱️ Cursor Tracking - Track mouse position, cursor types, and click events
  • 📷 Camera Recording - Capture silent camera video alongside screen recordings
  • 🔊 Audio Capture - Record microphone/system audio into synchronized companion files
  • 🚫 Automatic Overlay Exclusion - Overlay windows automatically excluded from recordings
  • Electron Compatible - Enhanced crash protection for Electron applications
  • 🎬 Multi-Window Recording - ✨ NEW! Record multiple windows/displays simultaneously

🎵 Granular Audio Controls

  • 🎤 Microphone Audio - Separate microphone control (default: off)
  • 🔊 System Audio - System audio capture (default: on)
  • 📻 Audio Device Listing - Enumerate available audio devices
  • 🎛️ Device Selection - Choose specific audio input devices

🔧 Smart Window Management

  • 📋 Window Discovery - List all visible application windows
  • 🎯 Automatic Coordinate Conversion - Handle multi-display coordinate systems
  • 📐 Display ID Detection - Automatically select correct display for window recording
  • 🖼️ Window Filtering - Smart filtering of recordable windows
  • 👁️ Preview Thumbnails - Generate window and display preview images

⚙️ Customization Options

  • 🎬 Quality Control - Adjustable recording quality presets
  • 🎞️ Frame Rate Control - Custom frame rate settings
  • 📁 Flexible Output - Custom output paths and formats
  • 🔐 Permission Management - Built-in permission checking

Note: The screen recording container remains silent. Audio is saved separately as temp_audio_<timestamp>.webm so you can remix microphone and system sound in post-processing.

ScreenCaptureKit Technology

This package leverages Apple's modern ScreenCaptureKit framework (macOS 12.3+) for superior recording capabilities:

  • 🎯 Native Overlay Exclusion: Overlay windows are automatically filtered out during recording
  • 🚀 Enhanced Performance: Direct system-level recording with optimized resource usage
  • 🛡️ Crash Protection: Advanced safety layers for Electron applications
  • 📱 Future-Proof: Built on Apple's latest screen capture technology
  • 🎨 Better Quality: Improved frame handling and video encoding

Note: For applications requiring overlay exclusion (like screen recording tools with floating UI), ScreenCaptureKit automatically handles window filtering without manual intervention.

Installation

npm install node-mac-recorder

Requirements

  • macOS 12.3+ (Monterey or later) - Required for ScreenCaptureKit
  • Node.js 14+
  • Xcode Command Line Tools
  • Screen Recording Permission (automatically requested)
  • CPU Architecture: Intel (x64) and Apple Silicon (ARM64) supported

Build Requirements

# Install Xcode Command Line Tools
xcode-select --install

# The package will automatically build native modules during installation

Apple Silicon Support: The package automatically builds for the correct architecture (ARM64 on Apple Silicon, x64 on Intel) during installation. No additional configuration required.

Quick Start

const MacRecorder = require("node-mac-recorder");

const recorder = new MacRecorder();

// Simple full-screen recording
await recorder.startRecording("./output.mov");
await new Promise((resolve) => setTimeout(resolve, 5000)); // Record for 5 seconds
await recorder.stopRecording();

Multi-Window Recording ✨ NEW!

Record multiple windows or displays simultaneously using child processes:

const MacRecorder = require("node-mac-recorder/index-multiprocess");

// Create separate recorders for each window
const recorder1 = new MacRecorder();
const recorder2 = new MacRecorder();

// Get available windows
const windows = await recorder1.getWindows();

// Record first window (e.g., Finder)
await recorder1.startRecording("window1.mov", {
  windowId: windows[0].id,
  frameRate: 30
});

// Wait for ScreenCaptureKit initialization
await new Promise(r => setTimeout(r, 1000));

// Record second window (e.g., Chrome)
await recorder2.startRecording("window2.mov", {
  windowId: windows[1].id,
  frameRate: 30
});

// Both recordings are now running in parallel! 🎉

// Stop both after 10 seconds
await new Promise(r => setTimeout(r, 10000));
await recorder1.stopRecording();
await recorder2.stopRecording();

// Cleanup
recorder1.destroy();
recorder2.destroy();

Key Benefits:

  • ✅ No native code changes required
  • ✅ Each recorder runs in its own process
  • ✅ True parallel recording
  • ✅ Separate output files
  • ✅ Independent control

See: MULTI_RECORDING.md for detailed documentation and examples.

API Reference

Constructor

const recorder = new MacRecorder();

Methods

startRecording(outputPath, options?)

Starts screen recording with the specified options.

await recorder.startRecording("./recording.mov", {
	// Audio Controls
	includeMicrophone: false, // Enable microphone (default: false)
	includeSystemAudio: true, // Enable system audio (default: true)
	audioDeviceId: "device-id", // Specific audio input device (default: system default)
	systemAudioDeviceId: "system-device-id", // Specific system audio device (auto-detected by default)

	// Display & Window Selection
	displayId: 0, // Display index (null = main display)
	windowId: 12345, // Specific window ID
	captureArea: {
		// Custom area selection
		x: 100,
		y: 100,
		width: 800,
		height: 600,
	},

	// Recording Options
	quality: "high", // 'low', 'medium', 'high'
	frameRate: 30, // FPS (15, 30, 60)
	captureCursor: false, // Show cursor (default: false)

	// Camera Capture
	captureCamera: true, // Enable simultaneous camera recording (video-only WebM)
	cameraDeviceId: "built-in-camera-id", // Use specific camera (optional)
});

stopRecording()

Stops the current recording.

const result = await recorder.stopRecording();
console.log("Recording saved to:", result.outputPath);
console.log("Camera clip saved to:", result.cameraOutputPath);
console.log("Audio clip saved to:", result.audioOutputPath);
console.log("Shared session timestamp:", result.sessionTimestamp);

The result object always contains cameraOutputPath and audioOutputPath. If either feature is disabled the corresponding value is null. The shared sessionTimestamp matches every automatically generated temp file name (temp_cursor_*, temp_camera_*, and temp_audio_*).

getWindows()

Returns a list of all recordable windows.

const windows = await recorder.getWindows();
console.log(windows);
// [
//   {
//     id: 12345,
//     name: "My App Window",
//     appName: "MyApp",
//     x: 100, y: 200,
//     width: 800, height: 600
//   },
//   ...
// ]

getDisplays()

Returns information about all available displays.

const displays = await recorder.getDisplays();
console.log(displays);
// [
//   {
//     id: 69733504,
//     name: "Display 1",
//     resolution: "2048x1330",
//     x: 0, y: 0
//   },
//   ...
// ]

getAudioDevices()

Returns a list of available audio input devices.

const devices = await recorder.getAudioDevices();
console.log(devices);
// [
//   {
//     id: "BuiltInMicDeviceID",
//     name: "MacBook Pro Microphone",
//     manufacturer: "Apple Inc.",
//     isDefault: true,
//     transportType: 0
//   },
//   ...
// ]

getCameraDevices()

Lists available camera devices with resolution metadata.

const cameras = await recorder.getCameraDevices();
console.log(cameras);
// [
//   {
//     id: "FaceTime HD Camera",
//     name: "FaceTime HD Camera",
//     position: "front",
//     maxResolution: { width: 1920, height: 1080, maxFrameRate: 60 }
//   },
//   ...
// ]

Camera Capture Helpers

  • setCameraEnabled(enabled) – toggles simultaneous camera recording (video-only)
  • setCameraDevice(deviceId) – selects the camera by unique macOS identifier
  • isCameraEnabled() – returns current camera toggle state
  • getCameraCaptureStatus() – returns { isCapturing, outputFile, deviceId, sessionTimestamp }

A typical workflow looks like this:

const cameras = await recorder.getCameraDevices();
const selectedCamera = cameras.find((camera) => camera.position === "front") || cameras[0];

recorder.setCameraDevice(selectedCamera?.id);
recorder.setCameraEnabled(true);

await recorder.startRecording("./output.mov");
// ...
const status = recorder.getCameraCaptureStatus();
console.log("Camera stream:", status.outputFile); // temp_camera_<timestamp>.webm
await recorder.stopRecording();

The camera clip is saved alongside the screen recording as temp_camera_<timestamp>.webm. The file contains video frames only (no audio). Use the same device IDs in Electron to power live previews (navigator.mediaDevices.getUserMedia({ video: { deviceId } })). On macOS versions prior to 15, Apple does not expose a WebM encoder; the module falls back to a QuickTime container while keeping the same filename, so transcode or rename if an actual WebM container is required.

See CAMERA_CAPTURE.md for a deeper walkthrough and Electron integration tips.

Audio Capture Helpers

  • setAudioDevice(deviceId) – select the microphone input
  • setSystemAudioEnabled(enabled) / isSystemAudioEnabled()
  • setSystemAudioDevice(deviceId) – prefer loopback devices when you need system audio only
  • getAudioCaptureStatus() – returns { isCapturing, outputFile, deviceIds, includeMicrophone, includeSystemAudio, sessionTimestamp }

See AUDIO_CAPTURE.md for a step-by-step guide on enumerating devices, enabling microphone/system capture, and consuming the audio companion files.

checkPermissions()

Checks macOS recording permissions.

const permissions = await recorder.checkPermissions();
console.log(permissions);
// {
//   screenRecording: true,
//   microphone: true,
//   accessibility: true
// }

getStatus()

Returns current recording status and options.

const status = recorder.getStatus();
console.log(status);
// {
//   isRecording: true,
//   outputPath: "./recording.mov",
//   cameraOutputPath: "./temp_camera_1720000000000.webm",
//   audioOutputPath: "./temp_audio_1720000000000.webm",
//   cameraCapturing: true,
//   audioCapturing: true,
//   sessionTimestamp: 1720000000000,
//   options: { ... },
//   recordingTime: 15
// }

getWindowThumbnail(windowId, options?)

Captures a thumbnail preview of a specific window.

const thumbnail = await recorder.getWindowThumbnail(12345, {
	maxWidth: 400, // Maximum width (default: 300)
	maxHeight: 300, // Maximum height (default: 200)
});

// Returns: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."
// Can be used directly in <img> tags or saved as file

getDisplayThumbnail(displayId, options?)

Captures a thumbnail preview of a specific display.

const thumbnail = await recorder.getDisplayThumbnail(0, {
	maxWidth: 400, // Maximum width (default: 300)
	maxHeight: 300, // Maximum height (default: 200)
});

// Returns: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."
// Perfect for display selection UI

Cursor Tracking Methods

startCursorCapture(outputPath)

Starts automatic cursor tracking and saves data to JSON file in real-time.

await recorder.startCursorCapture("./cursor-data.json");
// Cursor tracking started - automatically writing to file

stopCursorCapture()

Stops cursor tracking and closes the output file.

await recorder.stopCursorCapture();
// Tracking stopped, file closed

JSON Output Format:

[
	{
		"x": 851,
		"y": 432,
		"timestamp": 201,
		"cursorType": "default",
		"type": "move"
	},
	{
		"x": 851,
		"y": 432,
		"timestamp": 220,
		"cursorType": "pointer",
		"type": "mousedown"
	}
]

Cursor Types: default, pointer, text, grab, grabbing, ew-resize, ns-resize, crosshair
Event Types: move, mousedown, mouseup, rightmousedown, rightmouseup

Usage Examples

Window-Specific Recording

const recorder = new MacRecorder();

// List available windows
const windows = await recorder.getWindows();
console.log("Available windows:");
windows.forEach((win, i) => {
	console.log(`${i + 1}. ${win.appName} - ${win.name}`);
});

// Record a specific window
const targetWindow = windows.find((w) => w.appName === "Safari");
await recorder.startRecording("./safari-recording.mov", {
	windowId: targetWindow.id,
	includeSystemAudio: false,
	includeMicrophone: true,
	captureCursor: true,
});

await new Promise((resolve) => setTimeout(resolve, 10000)); // 10 seconds
await recorder.stopRecording();

Multi-Display Recording

const recorder = new MacRecorder();

// List available displays
const displays = await recorder.getDisplays();
console.log("Available displays:");
displays.forEach((display, i) => {
	console.log(`${i}: ${display.resolution} at (${display.x}, ${display.y})`);
});

// Record from second display
await recorder.startRecording("./second-display.mov", {
	displayId: 1, // Second display
	quality: "high",
	frameRate: 60,
});

await new Promise((resolve) => setTimeout(resolve, 5000));
await recorder.stopRecording();

Custom Area Recording

const recorder = new MacRecorder();

// Record specific screen area
await recorder.startRecording("./area-recording.mov", {
	captureArea: {
		x: 200,
		y: 100,
		width: 1200,
		height: 800,
	},
	quality: "medium",
	captureCursor: false,
});

await new Promise((resolve) => setTimeout(resolve, 8000));
await recorder.stopRecording();

Advanced System Audio Recording

const recorder = new MacRecorder();

// List available audio devices to find system audio devices
const audioDevices = await recorder.getAudioDevices();
console.log("Available audio devices:");
audioDevices.forEach((device, i) => {
	console.log(`${i + 1}. ${device.name} (ID: ${device.id})`);
});

// Find system audio device (like BlackHole, Soundflower, etc.)
const systemAudioDevice = audioDevices.find(device => 
	device.name.toLowerCase().includes('blackhole') ||
	device.name.toLowerCase().includes('soundflower') ||
	device.name.toLowerCase().includes('loopback') ||
	device.name.toLowerCase().includes('aggregate')
);

if (systemAudioDevice) {
	console.log(`Using system audio device: ${systemAudioDevice.name}`);
	
	// Record with specific system audio device
	await recorder.startRecording("./system-audio-specific.mov", {
		includeMicrophone: false,
		includeSystemAudio: true,
		systemAudioDeviceId: systemAudioDevice.id, // Specify exact device
		captureArea: { x: 0, y: 0, width: 1, height: 1 }, // Minimal video
	});
} else {
	console.log("No system audio device found. Installing BlackHole or Soundflower recommended.");
	
	// Record with default system audio capture (may not work without virtual audio device)
	await recorder.startRecording("./system-audio-default.mov", {
		includeMicrophone: false,
		includeSystemAudio: true, // Auto-detect system audio device
		captureArea: { x: 0, y: 0, width: 1, height: 1 },
	});
}

// Record for 10 seconds
await new Promise(resolve => setTimeout(resolve, 10000));
await recorder.stopRecording();

System Audio Setup:

For reliable system audio capture, install a virtual audio device:

  1. BlackHole (Free): https://github.com/ExistentialAudio/BlackHole
  2. Soundflower (Free): https://github.com/mattingalls/Soundflower
  3. Loopback (Paid): https://rogueamoeba.com/loopback/

These create aggregate audio devices that the package can detect and use for system audio capture.

Event-Driven Recording

const recorder = new MacRecorder();

// Listen to recording events
recorder.on("started", (outputPath) => {
	console.log("Recording started:", outputPath);
});

recorder.on("stopped", (result) => {
	console.log("Recording stopped:", result);
});

recorder.on("timeUpdate", (seconds) => {
	console.log(`Recording time: ${seconds}s`);
});

recorder.on("completed", (outputPath) => {
	console.log("Recording completed:", outputPath);
});

await recorder.startRecording("./event-recording.mov");

Window Selection with Thumbnails

const recorder = new MacRecorder();

// Get windows with thumbnail previews
const windows = await recorder.getWindows();

console.log("Available windows with previews:");
for (const window of windows) {
	console.log(`${window.appName} - ${window.name}`);

	try {
		// Generate thumbnail for each window
		const thumbnail = await recorder.getWindowThumbnail(window.id, {
			maxWidth: 200,
			maxHeight: 150,
		});

		console.log(`Thumbnail: ${thumbnail.substring(0, 50)}...`);

		// Use thumbnail in your UI:
		// <img src="${thumbnail}" alt="Window Preview" />
	} catch (error) {
		console.log(`No preview available: ${error.message}`);
	}
}

Display Selection Interface

const recorder = new MacRecorder();

async function createDisplaySelector() {
	const displays = await recorder.getDisplays();

	const displayOptions = await Promise.all(
		displays.map(async (display, index) => {
			try {
				const thumbnail = await recorder.getDisplayThumbnail(display.id);
				return {
					id: display.id,
					name: `Display ${index + 1}`,
					resolution: display.resolution,
					thumbnail: thumbnail,
					isPrimary: display.isPrimary,
				};
			} catch (error) {
				return {
					id: display.id,
					name: `Display ${index + 1}`,
					resolution: display.resolution,
					thumbnail: null,
					isPrimary: display.isPrimary,
				};
			}
		})
	);

	return displayOptions;
}

Cursor Tracking Usage

const MacRecorder = require("node-mac-recorder");

async function trackUserInteraction() {
	const recorder = new MacRecorder();

	try {
		// Start cursor tracking - automatically writes to file
		await recorder.startCursorCapture("./user-interactions.json");
		console.log("✅ Cursor tracking started...");

		// Track for 5 seconds
		console.log("📱 Move mouse and click for 5 seconds...");
		await new Promise((resolve) => setTimeout(resolve, 5000));

		// Stop tracking
		await recorder.stopCursorCapture();
		console.log("✅ Cursor tracking completed!");

		// Analyze the data
		const fs = require("fs");
		const data = JSON.parse(
			fs.readFileSync("./user-interactions.json", "utf8")
		);

		console.log(`📄 ${data.length} events recorded`);

		// Count clicks
		const clicks = data.filter((d) => d.type === "mousedown").length;
		if (clicks > 0) {
			console.log(`🖱️ ${clicks} clicks detected`);
		}

		// Most used cursor type
		const cursorTypes = {};
		data.forEach((item) => {
			cursorTypes[item.cursorType] = (cursorTypes[item.cursorType] || 0) + 1;
		});

		const mostUsed = Object.keys(cursorTypes).reduce((a, b) =>
			cursorTypes[a] > cursorTypes[b] ? a : b
		);
		console.log(`🎯 Most used cursor: ${mostUsed}`);
	} catch (error) {
		console.error("❌ Error:", error.message);
	}
}

trackUserInteraction();

Combined Screen Recording + Cursor Tracking

const MacRecorder = require("node-mac-recorder");

async function recordWithCursorTracking() {
	const recorder = new MacRecorder();

	try {
		// Start both screen recording and cursor tracking
		await Promise.all([
			recorder.startRecording("./screen-recording.mov", {
				captureCursor: false, // Don't show cursor in video
				includeSystemAudio: true,
				quality: "high",
			}),
			recorder.startCursorCapture("./cursor-data.json"),
		]);

		console.log("✅ Recording screen and tracking cursor...");

		// Record for 10 seconds
		await new Promise((resolve) => setTimeout(resolve, 10000));

		// Stop both
		await Promise.all([recorder.stopRecording(), recorder.stopCursorCapture()]);

		console.log("✅ Recording completed!");
		console.log("📁 Files created:");
		console.log("   - screen-recording.mov");
		console.log("   - cursor-data.json");
	} catch (error) {
		console.error("❌ Error:", error.message);
	}
}

recordWithCursorTracking();

Integration Examples

Electron Integration

// In main process
const { ipcMain } = require("electron");
const MacRecorder = require("node-mac-recorder");

const recorder = new MacRecorder();

ipcMain.handle("start-recording", async (event, options) => {
	try {
		await recorder.startRecording("./recording.mov", options);
		return { success: true };
	} catch (error) {
		return { success: false, error: error.message };
	}
});

ipcMain.handle("stop-recording", async () => {
	const result = await recorder.stopRecording();
	return result;
});

ipcMain.handle("get-windows", async () => {
	return await recorder.getWindows();
});

Express.js API

const express = require("express");
const MacRecorder = require("node-mac-recorder");

const app = express();
const recorder = new MacRecorder();

app.post("/start-recording", async (req, res) => {
	try {
		const { windowId, duration } = req.body;
		await recorder.startRecording("./api-recording.mov", { windowId });

		setTimeout(async () => {
			await recorder.stopRecording();
		}, duration * 1000);

		res.json({ status: "started" });
	} catch (error) {
		res.status(500).json({ error: error.message });
	}
});

app.get("/windows", async (req, res) => {
	const windows = await recorder.getWindows();
	res.json(windows);
});

Advanced Features

Automatic Display Detection

When recording windows, the package automatically:

  1. Detects Window Location - Determines which display contains the window
  2. Converts Coordinates - Translates global coordinates to display-relative coordinates
  3. Sets Display ID - Automatically selects the correct display for recording
  4. Handles Multi-Monitor - Works seamlessly across multiple displays
// Window at (-2000, 100) on second display
// Automatically converts to (440, 100) on display 1
await recorder.startRecording("./auto-display.mov", {
	windowId: 12345, // Package handles display detection automatically
});

Smart Window Filtering

The getWindows() method automatically filters out:

  • System windows (Dock, Menu Bar)
  • Hidden windows
  • Very small windows (< 50x50 pixels)
  • Windows without names

Performance Optimization

  • Native Implementation - Uses AVFoundation for optimal performance
  • Minimal Overhead - Low CPU usage during recording
  • Memory Efficient - Proper memory management in native layer
  • Quality Presets - Balanced quality/performance options

Testing

Run the included demo to test cursor tracking:

node cursor-test.js

This will:

  • ✅ Start cursor tracking for 5 seconds
  • 📱 Capture mouse movements and clicks
  • 📄 Save data to cursor-data.json
  • 🖱️ Report clicks detected

Troubleshooting

Permission Issues

If recording fails, check macOS permissions:

# Open System Preferences > Security & Privacy > Screen Recording
# Ensure your app/terminal has permission

Build Errors

# Reinstall with verbose output
npm install node-mac-recorder --verbose

# Clear npm cache
npm cache clean --force

# Ensure Xcode tools are installed
xcode-select --install

Recording Issues

  1. Empty/Black Video: Check screen recording permissions
  2. No Audio: Verify audio permissions and device availability
  3. Window Not Found: Ensure target window is visible and not minimized
  4. Coordinate Issues: Window may be on different display (handled automatically)

Debug Information

// Get module information
const info = recorder.getModuleInfo();
console.log("Module info:", info);

// Check recording status
const status = recorder.getStatus();
console.log("Recording status:", status);

// Verify permissions
const permissions = await recorder.checkPermissions();
console.log("Permissions:", permissions);

Performance Considerations

  • Recording Quality: Higher quality increases file size and CPU usage
  • Frame Rate: 30fps recommended for most use cases, 60fps for smooth motion
  • Audio: System audio capture adds minimal overhead
  • Window Recording: Slightly more efficient than full-screen recording
  • Multi-Display: No significant performance impact

File Formats

  • Output Format: MOV (QuickTime)
  • Video Codec: H.264
  • Audio Codec: AAC
  • Container: QuickTime compatible

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

MIT License - see LICENSE file for details.

Changelog

Latest Updates

  • Cursor Tracking: Track mouse position, cursor types, and click events with JSON export
  • Window Recording: Automatic coordinate conversion for multi-display setups
  • Audio Controls: Separate microphone and system audio controls
  • Display Selection: Multi-monitor support with automatic detection
  • Smart Filtering: Improved window detection and filtering
  • Performance: Optimized native implementation

Made for macOS 🍎 | Built with AVFoundation 📹 | Node.js Ready 🚀

🛡️ Permissions: Ensure your host application's Info.plist declares camera and microphone usage descriptions (see MACOS_PERMISSIONS.md).