How to Record a Web Page in the Browser Without Triggering Screen-Sharing Permission
Screen recording in JavaScript usually starts with navigator.mediaDevices.getDisplayMedia(). That API can capture the screen and pair nicely with MediaRecorder to save a video.
The problem is obvious: the browser will always show a permission dialog and ask the user what they want to share.

That makes it impossible to achieve a truly invisible recording flow.
If there is no permission prompt, then browser- or system-level screen capture is off the table. The only thing left is to work entirely inside the page with JavaScript.
Directly recording page content like a normal screen recorder is not straightforward. There is no ready-made solution that cleanly does this for arbitrary DOM content. But there is another angle: a video is just a sequence of frames. If the page can be captured repeatedly as images, those frames can then be turned into a video.
A simple demo page
Start with a small interactive page:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Canvas视频录制</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<main>
<div class="buttons">
<button class="start-btn">开始录制</button>
<button class="pause-btn">暂停录制</button>
<button class="resume-btn">继续录制</button>
<button class="stop-btn">结束录制</button>
</div>
<div id="box">
<section class="content">
<h2>TODO LIST</h2>
<div class="background-div">
<button class="background-btn">切换背景颜色</button>
</div>
<div id="todo-form">
<input type="text" class="input-field" placeholder="输入待办事项" />
<button type="submit" class="submit-btn">提交</button>
</div>
<div class="list"></div>
</section>
</div>
<img src="" alt="" class="hidden" />
</main>
<script
src="<https://cdn.bootcss.com/html2canvas/0.5.0-beta4/html2canvas.min.js>"
defer
></script>
<script src="canvas.js" defer></script>
</body>
</html>
The goal is not to capture the whole desktop, but to record what happens inside the page itself.
Step 1: turn the DOM into screenshots
A common tool for this is html2canvas. It can render HTML elements into a Canvas, including their CSS styling and visual appearance, and then export the result as an image.
That makes it possible to capture either a specific area or a larger section of the page.
const canvasFunction = () => {
html2canvas(box).then((canvas) => {
const imgStr = canvas.toDataURL('image/png')
img.src = imgStr
img.onload = function () {
ctx.drawImage(img, 0, 0, w, h)
}
})
}
This function snapshots the target element, converts it into an image, and draws that image onto another canvas.
Step 2: build a video stream from the canvas
The recording part relies on MediaRecorder, a browser API used for audio and video capture. It is commonly used with microphones, cameras, or screen streams, but here it can also record a stream produced by a canvas.
Some of its most useful methods are:
isTypeSupported()checks whether a given MIME type is supported on the current device.start()begins recording. It can also receive atimeslicein milliseconds, which causes the recording to be emitted in smaller chunks instead of one large block.pause()pauses recording.resume()continues a paused recording.stop()ends recording and triggersdataavailable, which returns the recordedBlobdata.
First create a hidden canvas to hold the screenshots generated by html2canvas, then use captureStream to turn that canvas into a live media stream:
const w = boxBoundingClientRect.width
const h = boxBoundingClientRect.height
const canvas = document.createElement('canvas')
canvas.setAttribute('id', 'canvas')
canvas.setAttribute('width', w)
canvas.setAttribute('height', h)
canvas.style.display = 'none'
box.appendChild(canvas)
const img = document.querySelector('img')
const ctx = canvas.getContext('2d')
const allChunks = []
const stream = canvas.captureStream(60) // 60 FPS recording 1秒60帧
The captureStream(60) call creates a 60 FPS stream from the canvas.
Step 3: record the canvas stream
Once the stream exists, it can be passed directly into MediaRecorder:
const recorder = new MediaRecorder(stream, {
mimeType: 'video/webm;codecs=vp9',
})
recorder.ondataavailable = (e) => {
allChunks.push(e.data)
}
Each time recording data becomes available, it is pushed into an array for later assembly.
Step 4: assemble and preview the video
When recording stops, merge all recorded chunks into a single Blob, generate a URL from it, and attach that video to the page:
recorder.stop()
const fullBlob = new Blob(allChunks)
const videoUrl = window.URL.createObjectURL(fullBlob)
const video = document.createElement('video')
video.controls = true
video.src = videoUrl
video.muted = true
video.autoplay = true
document.body.appendChild(video)
That gives an in-page preview of the final recording.
Or download the result directly
Instead of rendering the video element, it can also be downloaded immediately:
recorder.stop()
const fullBlob = new Blob(allChunks)
const videoUrl = window.URL.createObjectURL(fullBlob)
let link = document.createElement('a')
link.style.display = 'none'
let fullBlob = new Blob(allChunks)
let downloadUrl = window.URL.createObjectURL(fullBlob)
link.href = downloadUrl
link.download = 'canvas-video.mp4'
document.body.appendChild(link)
link.click()
link.remove()
Capturing strategy: event-driven or continuous
To reduce resource usage, screenshots do not have to be taken continuously. A practical approach is to call html2canvas only when something on the page changes, such as a button click or text input.
If a continuous recording effect is needed, requestAnimationFrame can be used to capture frames in real time.
What this approach can and cannot do
This method makes recording possible without triggering the browser's screen-sharing permission dialog, but only for content inside the current web page. It cannot capture anything outside the page itself.
The demo records DOM changes. If a visible mouse trail is also needed, one option is to add a DOM element that follows the cursor so it becomes part of the recorded frames.