Frontend Development 28 min read

User Behavior Recording Techniques: Video, Screenshot, and DOM Snapshot (rrweb) Comparison and Implementation

This article examines various user behavior recording methods—including WebRTC video capture, canvas-based screenshot recording, and DOM snapshot recording with rrweb—detailing their technical implementations, advantages, limitations, and suitable application scenarios for product analysis, debugging, and automated testing.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
User Behavior Recording Techniques: Video, Screenshot, and DOM Snapshot (rrweb) Comparison and Implementation

Problem Background

In many projects we rely on click and page‑view (PV) tracking to collect user actions, but these points cannot capture contextual usage scenarios needed by product, development, and testing teams.

Product : Need the real usage path to verify that user behavior matches design expectations.

Development : System alerts indicate an error but not the reproduction steps, especially for intermittent issues.

Testing : When users report bugs, the exact steps are often unknown, leading to high communication cost.

Therefore we need a way to record a continuous sequence of user actions—including clicks, scrolls, inputs—and replay them faithfully.

Technical Solutions

2.1 Video Recording (WebRTC)

WebRTC provides real‑time media streams. The relevant APIs are mediaDevices.getDisplayMedia() , new MediaRecorder() , and the ondataavailable event.

Recording flow:

Call mediaDevices.getDisplayMedia() to obtain screen stream after user permission.

Create a new MediaRecorder() for the stream.

Listen to ondataavailable to collect Blob data.

<
template
>
<
video
ref
=
\"playerRef\"
>
</
video
>
<
button
@
click
=
\"handleStart\"
>
开启录制
</
button
>
<
button
@
click
=
\"handlePause\"
>
暂停录制
</
button
>
<
button
@
click
=
\"handleResume\"
>
继续录制
</
button
>
<
button
@
click
=
\"handleStop\"
>
结束录制
</
button
>
<
button
@
click
=
\"handleReplay\"
>
播放录制
</
button
>
<
button
@
click
=
\"handleReset\"
>
重置内容
</
button
>
</
template
>
<
script
lang
=
\"ts\"
setup
>
import
{ ref, reactive }
from
\'vue\'
;
const
playerRef = ref();
const
state = reactive({
mediaRecorder
:
null
as
null
| MediaRecorder,
blobs
: []
as
Blob[],
});
// 开始录制
const
handleStart =
async
() => {
const
stream =
await
navigator.mediaDevices.getDisplayMedia();
state.mediaRecorder =
new
MediaRecorder(stream, {
mimeType
:
\'video/webm\'
,
});
state.mediaRecorder.addEventListener(\'dataavailable\', (e: BlobEvent) => {
state.blobs.push(e.data);
});
state.mediaRecorder?.start();
};
// canvas录制(特殊处理)
const
handleCanvasRecord =
()
=>
{
const
stream = canvas.captureStream(
60
);
// 60 FPS recording
const
recorder =
new
MediaRecorder(stream, {
mimeType
:
\'video/webm;codecs=vp9\'
,
});
recorder.ondataavailable =
(e) =>
{
state.blobs.push(e.data);
};
}
// 暂停录制
const
handlePause =
()
=>
{ state.mediaRecorder?.pause() };
// 继续录制
const
handleResume =
()
=>
{ state.mediaRecorder?.resume() };
// 停止录制
const
handleStop =
()
=>
{ state.mediaRecorder?.stop() };
// 播放录制
const
handleReplay =
()
=>
{
if
(state.blobs.length ===
0
|| !playerRef.value)
return
;
const
blob =
new
Blob(state.blobs, {
type
:
\'video/webm\'
});
playerRef.value.src = URL.createObjectURL(blob);
playerRef.value.play();
};
const
handleReset =
()
=>
{
state.blobs = [];
state.mediaRecorder =
null
;
playerRef.value.src =
null
;
};
const
handleDownload =
()
=>
{
if
(state.blobs.length ===
0
)
return
;
const
blob =
new
Blob(state.blobs, {
type
:
\'video/webm\'
});
const
url = URL.createObjectURL(blob);
const
a =
document
.createElement(\'a\');
a.href = url;
a.style.display = \'none\';
a.download = \'record.webm\';
a.click();
};
</
script
>

Issues with this approach:

Requires user consent and visible UI, making recording perceptible.

Cannot mask sensitive data in the video.

Browser compatibility varies.

2.2 Page Screenshot (html2canvas)

By periodically capturing canvas snapshots with html2canvas and playing them back at the same frame rate, we can simulate recording.

Problems:

Canvas cannot capture animations, may produce layout shifts.

High performance cost and large resource size (e.g., 200 KB per image).

Masking elements removes them entirely, affecting layout.

<
template
>
<
el-button
@
click
=
\"handleStart\"
>
开启录制
</
el-button
>
<
el-button
@
click
=
\"handleStop\"
>
停止录制
</
el-button
>
<
el-button
@
click
=
\"handleReplay\"
>
播放录制
</
el-button
>
<
img
:src=
\"state.imgs[state.num ?? 0]\"
/>
</
template
>
<
script
lang
=
\"ts\"
setup
>
import
{ reactive }
from
\'vue\'
;
import
html2canvas
from
\'html2canvas\'
;
const
state = reactive({
visible
:
false
,
imgs
: []
as
string[],
num
:
0
,
recordInterval
:
null
as
any,
replayInterval
:
null
as
any,
});
const
FPS =
30
;
const
interval =
1000
/ FPS;
const
handleStart =
async
() => {
handleReset();
state.recordInterval = setInterval(() => {
if
(state.imgs.length >
100
) {
handleStop();
return
;
}
html2canvas(document.body).then((canvas: any) => {
const
img = canvas.toDataURL();
state.imgs.push(img);
});
}, interval);
};
const
handleStop = () => {
state.recordInterval && clearInterval(state.recordInterval);
};
const
handleReplay =
async
() => {
state.recordInterval && clearInterval(state.recordInterval);
state.num =
0
;
state.visible =
true
;
state.replayInterval = setInterval(() => {
if
(state.num >= state.imgs.length -
1
) {
clearInterval(state.replayInterval);
return
;
}
state.num++;
}, interval);
};
const
handleReset = () => {
state.imgs = [];
state.recordInterval =
null
;
state.replayInterval =
null
;
state.num =
0
;
};

2.3 DOM Snapshot Recording (rrweb)

rrweb records DOM changes as JSON snapshots and replays them. It consists of three parts: rrweb‑snapshot (snapshot & rebuild), rrweb (record & replay), and rrweb‑player (UI controls).

Recording process: a full‑page snapshot is taken, then mutation observers and event listeners capture incremental changes.

Replay process: snapshots are rebuilt in a sandboxed iframe, and events are replayed according to timestamps.

<template>
<button @click=\"handleStart\">开启录制</button>
<button @click=\"handleStop\">结束录制</button>
<button @click=\"handleReplay\">播放录制</button>
<div class=\"replay\" ref=\"replayRef\"></div>
</template>
<script lang=\"ts\" setup>
import { reactive, ref } from \"vue\";
import * as rrweb from \"rrweb\";
import rrwebPlayer from \"rrweb-player\";
import \"rrweb-player/dist/style.css\";
const replayRef = ref();
const state = reactive({
events: [] as any[],
stopFn: null as any,
});
const handleStart = () => {
state.stopFn = rrweb.record({
emit(event) {
if (state.events.length > 100) {
// 当事件数量大于 100 时停止录制
handleStop();
} else {
state.events.push(event);
}
},
});
ElMessage(\'开始录制\');
};
const handleStop = () => {
state.stopFn?.();
ElMessage(\'已停止录制\');
};
const handleReplay = () => {
new rrwebPlayer({
target: replayRef.value, // 可以自定义 DOM 元素
props: { events: state.events },
});
};
</script>

2.4 Solution Comparison

Aspect

Video Recording

Page Screenshot

DOM Snapshot

Open‑source library

WebRTC native

html2canvas

rrweb

User perception

Visible

Invisible

Invisible

Output size

Large

Large

Relatively small

Compatibility

Depends on API support

Partial

Good

Operability

Weak

Weak

Strong (supports masking, encryption)

Replay fidelity

Lossy, set at recording

Lossy, set at recording

High fidelity

Application Scenarios

#

Scenario

Description

1

Product feature analysis

Record real user paths to evaluate feature usage and guide optimization.

2

User interview recording

Capture actual user interactions during interviews for later review.

3

Issue reproduction

Preserve the exact steps leading to a bug to reduce communication overhead.

4

Automated test case generation

Convert recorded actions into test scripts.

5

Other

Case review, behavior monitoring, process quality inspection, etc.

Platform Solutions

Sentry and Hotjar both provide recording & replay features built on rrweb, with additional analytics such as heatmaps.

Conclusion

Dom‑snapshot recording with rrweb offers the best balance of fidelity, size, and operability compared with video capture and canvas screenshots, and is widely adopted in commercial solutions.

References

User behavior recording techniques – Juejin

Frontend recording and replay system – Juejin

Browser recording research – Zhihu

WebRTC quick start – Juejin

frontenduser behaviorVuerecordingWebRTCrrweb
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.