Skip to content

aslanon/node-mac-recorder

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

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).

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •