Architecting Session Reconstruction: How Microsoft Clarity Captures DOM Mutations
Clarity records DOM mutations and replays them against a snapshot rather than capturing video. Here is the architecture, the masking boundary, and the checks that catch silent failure.
19 Jan 2026, 21:57 UTC

What a session recording actually is
Session replay is often described as "recording video of the user's screen." That framing leads to the wrong architecture. A ten-minute video is tens to hundreds of megabytes, cannot be indexed, and cannot be redacted after the fact. Microsoft Clarity takes the other route: it captures the page's structure and the changes made to it, then rebuilds the visual state later.
The practical takeaway is that Clarity's recording is a DOM mutation log replayed against an initial snapshot, not a pixel stream. That single fact determines what you can expect from it, how you test it, and where it fails quietly.
Requirements that shape the design
- Bandwidth per session has to stay small enough that the script is not a performance liability.
- Capture must not block the main thread, or it will distort the interaction timings it is trying to measure.
- Personally identifiable information (PII) must not leave the browser, because anything transmitted is already outside your control.
- Reconstruction must work later, on a server, without access to the original application session.
The smallest suitable design
1. Capture: one snapshot plus deltas
On load, the script serializes the current DOM — the browser's in-memory tree of HTML elements — into a compact snapshot. From that point on, a MutationObserver reports changes: nodes added or removed, attributes changed, text rewritten. User events such as clicks, scrolls and keystrokes are timestamped and associated with the element that received them.
Only deltas travel. A page that never changes produces almost no traffic, which is why this approach scales where video does not.
2. The trust boundary sits in the browser
Masking has to happen client-side, before the payload is queued for transmission. Once data is on the wire, no server-side filter can undo the disclosure.
- Password inputs are masked by default.
- Additional elements can be marked for masking or for complete omission through project settings.
- Masked values are substituted during serialization, so the original text is never placed in the payload.
This is a configuration boundary, not a guarantee. A sensitive value rendered into a plain <div>, a data- attribute, or a third-party widget that was never marked will be captured like any other text.
3. Replay: applying mutations in order
The backend does not play a video. It loads the stored snapshot, applies the mutation list in timestamp order, and renders the result in a viewer. Seeking to a moment means replaying from the snapshot up to that point, which is why a corrupt or missing snapshot makes the whole session unrecoverable.
Operational checks
Confirming capture and transmission
Run this in a browser on a staging page that already loads the Clarity snippet. No elevated permissions are required; DevTools access is enough.
1. Open DevTools (F12) and switch to the Network tab.
2. Reload the page and filter requests by the ingestion host
named in your Clarity snippet.
3. Click, scroll and type for about 30 seconds.
4. Look for periodic POST requests and note their payload sizes.
5. Open the Clarity dashboard and confirm the session appears.
Expected result: requests continue while you interact, and payload sizes stay in the kilobyte range rather than the megabyte range. If there are no requests at all, check the console for Content Security Policy violations before assuming a product defect — a blocked script produces total data loss for those users, not partial data.
Confirming masking
Type a unique marker such as MASK-CHECK-9271 into a field you believe is masked, then open the recorded session and search for that string. If it appears in plain text, the masking configuration is wrong. Repeat for every field type you care about, including fields rendered by third-party components, because those are the ones most often missed.
Failure modes
| Scenario | Why it breaks | What you see |
|---|---|---|
| Canvas or WebGL content | Drawing happens in a pixel buffer, not the DOM | Blank or static regions in the replay |
| Closed Shadow DOM | Encapsulated subtrees are not observable from outside | Missing components, layout gaps |
| Strict CSP | The ingestion script itself is blocked | No sessions at all for affected users |
| Unmarked sensitive text | Masking only applies to configured targets | Raw values visible in the replay |
| Server-side CSS change | The DOM is unchanged, so no mutation is logged | Replay renders with stale styling |
Conditions that would change the design
If a product's core value sits inside a canvas, a WebGL scene, or tightly encapsulated web components, mutation replay will not represent it faithfully. At that point the design has to shift toward hybrid capture: serialize the internal state of those specific components on a schedule, or capture periodic screenshots of just those regions, and accept the extra bandwidth in exchange for coverage.
The other trigger is compliance. If a page routinely renders regulated data into unmarked elements, no amount of tuning fixes the boundary — the correct change is to move that rendering behind an explicitly masked component or to exclude the route from capture entirely.
Limitations and what to verify
Endpoint names, default sampling rates, retention windows and default masking behaviour are product decisions that change over time. Treat the description above as architecture-level and confirm current values against Microsoft's documentation for your own tenant before relying on them in a compliance review.
The two checks worth repeating after every significant site change are the ones above: interaction produces network traffic, and a known marker string does not appear in the replay. Both are cheap, and both catch the failure modes that otherwise go unnoticed until someone reviews a recording.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.