Build an Electron App that can capture and record video files from your desktop. 676 words.
Last Updated
Electron opens the world of desktops apps to the average JavaScript developer. It wraps Chromium with Node.js, providing a browser for building UIs and Node for low-level system operations.
The following project tutorial demonstrates how to build a simple screen recorder with Electron. The app can retrieve the available screens from the system, turn the screen into a video feed, then record and save the raw video file to the system.
Initial Setup
Electron Forge
Create a new app with Electron Forge - it provides a solid starting point for building and distributing the app.
npx create-electron-app my-app
cd my-app
npm start
💡 Tip - Enter rs
into the terminal to restart the app after making code changes.
HTML Markup
The HTML contains a <video>
element to preview the output from a screen and provides buttons to start/stop recording.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Simple Screen Recorder</title>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bulma@0.8.0/css/bulma.min.css"
/>
<link rel="stylesheet" href="index.css" />
<script defer src="render.js"></script>
</head>
<body class="content">
<h1>âš¡ Electron Screen Recorder</h1>
<video></video>
<button id="startBtn" class="button is-primary">Start</button>
<button id="stopBtn" class="button is-warning">Stop</button>
<hr />
<button id="videoSelectBtn" class="button is-text">
Choose a Video Source
</button>
</body>
</html>
Include Node in Electron’s Render Process
In order to use Node in Electron’s frontend render process, we need to need to add the following config option:
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: { /// <-- update this option
nodeIntegration: true
}
});
Screen Recorder
Create a file named src/render.js
. All code in this section runs in this file.
Get Available Screens
How do we access the available windows or screens to record? Electron has a built-in desktopCapturer that returns a list of the user’s screens.
const videoSelectBtn = document.getElementById('videoSelectBtn');
videoSelectBtn.onclick = getVideoSources;
const { desktopCapturer, remote } = require('electron');
const { Menu } = remote;
// Get the available video sources
async function getVideoSources() {
const inputSources = await desktopCapturer.getSources({
types: ['window', 'screen']
});
}
Display a Popup Menu
At this point we have a list of screens, but need a UI element for the user to select one. This is a good use-case for a popup menu.
const { desktopCapturer, remote } = require('electron');
const { Menu } = remote;
async function getVideoSources() {
const inputSources = await desktopCapturer.getSources({
types: ['window', 'screen']
});
const videoOptionsMenu = Menu.buildFromTemplate(
inputSources.map(source => {
return {
label: source.name,
click: () => selectSource(source)
};
})
);
videoOptionsMenu.popup();
}
Preview Video Stream
Once a screen is selected it should be previewed in the video element. The code below uses navigator.mediaDevices.getUserMedia
to turn the screen into a raw video feed.
A MediaRecorder instance is created to record the stream as a webm
video file that can be played back.
let mediaRecorder; // MediaRecorder instance to capture footage
const recordedChunks = [];
// Change the videoSource window to record
async function selectSource(source) {
videoSelectBtn.innerText = source.name;
const constraints = {
audio: false,
video: {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: source.id
}
}
};
// Create a Stream
const stream = await navigator.mediaDevices
.getUserMedia(constraints);
// Preview the source in a video element
videoElement.srcObject = stream;
videoElement.play();
// Create the Media Recorder
const options = { mimeType: 'video/webm; codecs=vp9' };
mediaRecorder = new MediaRecorder(stream, options);
// Register Event Handlers
mediaRecorder.ondataavailable = handleDataAvailable;
mediaRecorder.onstop = handleStop;
}
Record and Save a Video File
The final step is to give the user control over the recording and saving of a video file.
const { writeFile } = require('fs');
const { dialog, Menu } = remote;
const startBtn = document.getElementById('startBtn');
startBtn.onclick = e => {
mediaRecorder.start();
startBtn.classList.add('is-danger');
startBtn.innerText = 'Recording';
};
const stopBtn = document.getElementById('stopBtn');
stopBtn.onclick = e => {
mediaRecorder.stop();
startBtn.classList.remove('is-danger');
startBtn.innerText = 'Start';
};
// Captures all recorded chunks
function handleDataAvailable(e) {
console.log('video data available');
recordedChunks.push(e.data);
}
// Saves the video file on stop
async function handleStop(e) {
const blob = new Blob(recordedChunks, {
type: 'video/webm; codecs=vp9'
});
const buffer = Buffer.from(await blob.arrayBuffer());
const { filePath } = await dialog.showSaveDialog({
buttonLabel: 'Save video',
defaultPath: `vid-${Date.now()}.webm`
});
console.log(filePath);
writeFile(filePath, buffer, () => console.log('video saved successfully!'));
}