paint-brush
Here's How I Built a Video, Audio, and Screen Recorder Web App with JavaScriptby@dhruv479
1,260 reads
1,260 reads

Here's How I Built a Video, Audio, and Screen Recorder Web App with JavaScript

by Dhruv BansalFebruary 19th, 2021
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Dhruv Bansal created a free simple recorder that supports Audio, Video, and Screen Recording available at http://recorder.druv479.dev.com. He explains how to use the MediaRecorder API to record audio, video, and screen recording. The code is written in JavaScript and can be downloaded from the browser using the getUserMedia function to get access to resources on the user’s machine. After calling this, we will get a prompt asking for permission in the browser and after confirmation, we can access those resources.

Company Mentioned

Mention Thumbnail
featured image - Here's How I Built a Video, Audio, and Screen Recorder Web App with JavaScript
Dhruv Bansal HackerNoon profile picture

In this pandemic, a lot has changed with work culture. Something we can’t get rid of yet is Remote Meetings. While at one of those meetings, I got curious about the Google meeting platform and after some search came to know what our browsers are capable of (inbuilt browser APIs).

So, I planned to develop a free simple recorder that supports Audio, Video, and Screen Recording available at http://recorder.dhruv479.dev

To proceed further, you should be acquainted with some terms:

MediaRecorder: The MediaRecorder interface of the MediaStream API provides functionality to easily record media ref: MDN

MediaDevices: The MediaDevices interface provides access to connected media input devices like cameras and microphones, as well as screen-sharing ref: MDN

Ok, ready to roll? let’s add some code!

Recording Handler: At first, we will write a function that intakes the stream and mimeType, collect the data chunks and then enable downloading the file on the local machine from the browser.

const handleRecord = function ({stream, mimeType}) {
  // to collect stream chunks
  let recordedChunks = [];
  stopped = false;
  const mediaRecorder = new MediaRecorder(stream);

  mediaRecorder.ondataavailable = function (e) {
    if (e.data.size > 0) {
      recordedChunks.push(e.data);
    }
    // shouldStop => forceStop by user
    if (shouldStop === true && stopped === false) {
      mediaRecorder.stop();
      stopped = true;
    }
  };
  mediaRecorder.onstop = function () {
    const blob = new Blob(recordedChunks, {
      type: mimeType
    });
    recordedChunks = []
    const filename = window.prompt('Enter file name'); // input filename from user for download
    downloadLink.href = URL.createObjectURL(blob); // create download link for the file
    downloadLink.download = `${filename}.webm`; // naming the file with user provided name
    stopRecord();
  };

  mediaRecorder.start(200); // here 200ms is interval of chunk collection
};

First, create a MediaRecorder instance with the input stream. Then, add events for ondataavailable (when data comes in-stream) and onstop (when the stream stops).

After chunk is available, it is pushed in the array and after the stop event, all the chunks are put together to form a Blob specifying the mimeType.

Later, URL is created for the blob object and can be downloaded from the browser.

Audio Recorder: Going forward, we will write a function to invoke the audio recording that further calls handleRecord with the required parameters.

async function recordAudio() {
  const mimeType = 'audio/webm';
  shouldStop = false;
  const stream = await navigator.mediaDevices.getUserMedia({audio: true});
  handleRecord({stream, mimeType})
}

Here, we will use mediaDevices.getUserMedia to get access to resources on the user’s machine. After calling this, we will get a prompt asking for permission in the browser and after confirmation, we can access those resources.

Also, we can pass additional parameters to the constraints like audio: { echoCancellation: true } which reduces the sound echo.

Video Recorder: Similar to Audio Recording, we need to add video in the parameter of the getUserMedia function.

async function recordVideo() {
  const mimeType = 'video/webm';
  shouldStop = false;
  const constraints = {
    audio: true,
    video: true,
  };
  const stream = await navigator.mediaDevices.getUserMedia(constraints);
  handleRecord({stream, mimeType})
}

After adding video constraint in getUserMedia, you will get an additional prompt to camera permission and after the confirmation, guess what, we can access the camera stream. Also, additional params can be passed to video such as: video: { width: {min: 640}, height: {min: 480}}

Screen Recorder: Amongst all, this is the most interesting, as the display stream is provided by getDisplayMedia, and the audio is provided by getUserMedia. So, we need to merge different streams here if need an audio background for the screen record.

async function recordScreen() {
  const mimeType = 'video/webm';
  shouldStop = false;
  const constraints = {
    video: true
  };
  const displayStream = await navigator.mediaDevices.getDisplayMedia({video: true, audio: true});
  // voiceStream for recording voice with screen recording
  const voiceStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
  let tracks = [...displayStream.getTracks(), ...voiceStream.getAudioTracks()]
  const stream = new MediaStream(tracks);
  handleRecord({stream, mimeType})
}

We merged the different streams to create a new stream and passed it to the handleRecord function. Here also, we can add more specific constraints as per the need.

Constraints passed to capture media can be more specific like resolution, echo, cursor instead of true/false.

Conclusion

Voila, well the JavaScript part is done 😁. We just need to add some basic
HTML to invoke those functions, stop recording, and of course, download
the file.

Here is a reference to the complete code:

https://github.com/dhruv479/recorder/blob/master/index.html

Also published at https://dhruv479.medium.com/583584dd3c75