- learning
- guides
- #screen-recorder
Screen recorder with JS
With jQuery File Uploader and some javascript, you can create a screen recorder that can record any browser tab or the entire screen. This guide will explain how to add screen recording to Uploadcare jQuery File Uploader widget via a custom tab. As a result, your users can take screen recordings with a widget added to your site. Video files will be uploaded directly to your Uploadcare project.
The widget offers an API that allows you to add new tabs and implement custom logic, e.g., add custom upload sources. Learn more about custom tabs from Uploadcare jQuery File Uploader docs.
We'll use plain HTML, CSS, and JS, so no tricky setup is required. You need to create three files in your project folder:
- index.html
- style.css
- app.js
index.html
Set up the index.html
file first:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>My web app</title>
{/* Link our styles.css file */}
<link rel="stylesheet" href="style.css" />
{/* Add the uploadcare.js library to install [the widget](/docs/uploads/file-uploader/#install) */}
<script
src="https://ucarecdn.com/libs/widget/3.x/uploadcare.full.min.js"
{/* add the uploadcare.js library to install [the widget](/docs/uploads/file-uploader/#install) */}
charset="utf-8"
></script>
{/* Add a script tag with global widget [settings](/docs/uploads/file-uploader/#config-globals) */}
{/* Replace the YOUR_PUBLIC_KEY with [yours API keys](/docs/start/settings/#keys-public) */}
<script>
UPLOADCARE_PUBLIC_KEY = "YOUR_PUBLIC_KEY";
UPLOADCARE_TABS = "recorder file camera";
UPLOADCARE_PREVIEW_STEP = true;
</script>
</head>
<body>
{/* Add an SVG icon that we’ll be using as the custom tab icon in the widget */}
<svg width="0" height="0" style="position: absolute;">
<symbol id="uploadcare--icon-recorder" viewBox="0 0 32 32">
<path
d="M 16 22.928 L 23.416 27.4 L 21.454 18.965 L 28 13.292 L 19.37 12.552 L 16 4.6 L 12.629 12.552 L 4 13.292 L 10.546 18.965 L 8.584 27.4 Z"
/>
</symbol>
</svg>
<p>
{/* Add a hidden input element with role="uploadcare-uploader" */}
{/* Uploadcare.js library will turn it into the uploader button automatically */}
<input
type="hidden"
role="uploadcare-uploader"
name="file_upload"
/>
</p>
{/* Link our app.js file */}
<script src="app.js"></script>
</body>
</html>
style.css
Set up the style.css
file:
.start-btn,
.start-btn:hover {
min-width: 200px;
max-width: 300px;
font-size: 1rem;
}
.uploadcare--tab_name_recorder {
flex-direction: column;
align-items: center;
}
.uploadcare--widget__button {
padding: 0.75em;
}
We'll be adding some additional UI elements to our custom tab, and the CSS above is all about positioning and styling them.
app.js
Finally, set up the app.js
file to make things work.
We'll be adding code step by step and explaining each step in detail.
Let's add the getSupportedMime
function. It must detect the most suitable
video file type and codec for the user's browser. We'll not go into detail
about how it works, but If you'd like to learn more about this, check out
this thread on Stackoverflow.
function getSupportedMime() {
const types = ["webm", "ogg", "mp4", "x-matroska"];
const codecs = [
"vp9",
"vp9.0",
"vp8",
"vp8.0",
"avc1",
"av1",
"h265",
"h.265",
"h264",
"h.264",
"opus",
"pcm",
"aac",
"mpeg",
"mp4a"
];
const isSupported = MediaRecorder.isTypeSupported;
const supported = [];
types.forEach((type) => {
const mimeType = `video/${type}`;
codecs.forEach((codec) =>
[
`${mimeType};codecs=${codec}`,
`${mimeType};codecs:${codec.toUpperCase()}`,
`${mimeType}`
].forEach((variation) => {
if (isSupported(variation)) supported.push(variation);
})
);
if (isSupported(mimeType)) supported.push(mimeType);
});
return supported[0];
}
Now let's register a new tab:
uploadcare.registerTab("recorder", screenRecorderTab);
screenRecorderTab
is a function invoked when a user opens the widget.
We need to define it and implement the tab logic inside:
function screenRecorderTab(container, button, dialog, settings) {
// Add UI elements to the tab
button[0].title = "Screen Recorder";
const header = document.createElement("h1");
header.innerText = "Screen recorder";
header.style.textAlign = "center";
const startBtn = document.createElement("button");
startBtn.innerText = "Record screen";
startBtn.classList.add(
"start-btn",
"uploadcare--widget__button",
"uploadcare--widget__button_type_open"
);
container[0].appendChild(header);
container[0].appendChild(startBtn);
// Check if browser supports screen recording
if (!navigator.mediaDevices.getDisplayMedia) {
startBtn.innerText = "Screen recording is not supported";
startBtn.disabled = true;
return;
} else {
startBtn.addEventListener("click", recordScreen);
}
// Screen recording implementation
async function recordScreen() {
const stream = await navigator.mediaDevices.getDisplayMedia({
video: true
});
const mime = getSupportedMime();
let extension = mime.split("/").pop().split(";")[0];
// Change extension to MKV if mime-type x-matroska
if (extension === "x-matroska") {
extension = "mkv";
}
// Initiate MediaRecorder
const mediaRecorder = new MediaRecorder(stream, {
mimeType: mime
});
// Disable record button and change its text when recording in progress
startBtn.innerText = "Recording...";
startBtn.disabled = true;
const chunks = [];
mediaRecorder.addEventListener("dataavailable", (e) => {
chunks.push(e.data);
});
mediaRecorder.addEventListener("stop", () => {
let blob = new Blob(chunks, {
type: chunks[0].type
});
let file = new File([blob], `recording.${extension}`);
let upload = uploadcare.fileFrom("object", file);
startBtn.innerText = "Record Screen";
startBtn.disabled = false;
dialog.addFiles([upload]);
});
//Start the recorder
mediaRecorder.start();
}
}
The callback function screenRecorderTab
allows us to access the tab container
and button via its container
and button
arguments, so we don't have to use
extra selectors. Also, we check if the browser supports
the required API (getDisplayMedia
):
if (!navigator.mediaDevices.getDisplayMedia) {
startBtn.innerText = "Screen recording is not supported";
startBtn.disabled = true;
return;
} else {
startBtn.addEventListener("click", recordScreen);
}
If so, we add a click event listener to the Record Screen button that calls
the recordScreen
function.
We'll take a look at the function soon. So far, our custom tab should look like
this:
Now let's explore the recordScreen
function.
We'll be using the following browser APIs to get and record the video stream
from the screen:
As a first step, we need to get a video stream from the screen:
const stream = await navigator.mediaDevices.getDisplayMedia({
video: true
});
Then, using our utility function getSupportedMime, we pick the most suitable mime type and specify the extension for the output video file:
const mime = getSupportedMime();
let extension = mime.split("/").pop().split(";")[0];
// Change extension to MKV if mime-type x-matroska
if (extension === "x-matroska") {
extension = "mkv";
}
Now we can initiate MediaRecorder
and pass the stream to it:
const mediaRecorder = new MediaRecorder(stream, {
mimeType: mime
});
Further, once the recording process has started, we disable the Record Screen button and change its text to “Recording…”:
startBtn.innerText = "Recording...";
startBtn.disabled = true;
Data from the video stream comes in chunks, and we need a variable to store it. Also, we add an event listener that will push every new chunk of video data to our variable:
const chunks = [];
mediaRecorder.addEventListener("dataavailable", (e) => {
chunks.push(e.data);
});
A user can stop the recording at any time, and when this happens, we need to build a file object from the video data chunks:
mediaRecorder.addEventListener("stop", () => {
let blob = new Blob(chunks, {
type: chunks[0].type
});
let file = new File([blob], `recording.${extension}`);
let upload = uploadcare.fileFrom("object", file);
startBtn.innerText = "Record Screen";
startBtn.disabled = false;
dialog.addFiles([upload]);
});
Let's see what this piece of code does: once the user stops recording,
we create a new Blob from our chunks and set its type as the type of
the first element in the chunks
array. Then we create a new File from
the blob and put its name to “recording” and its extension to the extension
we specified before based on the mime type. At this point, we can upload
the file to Uploadcare by calling jQuery File Uploader API docs:
let upload = uploadcare.fileFrom("object", file);
Then we change the state of the Record Screen button and add the file to the widget's dialog with:
dialog.addFiles([upload]);
The remaining line looks like this:
mediaRecorder.start();
It starts the MediaRecorder when the Record Screen button is clicked.
Recording continues until the Stop button is pressed. After the recording is stopped, the video file is added to the Uploadсare project and is available on the Files tab.