How Flutter web renders
Flutter web can render through different paths. The production default for most apps is CanvasKit, a WebAssembly build of Skia that draws the entire UI to a WebGL canvas. The newer Skwasm renderer paints to canvas via WebAssembly too. Either way, the output is pixels on a canvas, not an element tree.
This is by design: it gives Flutter pixel-perfect, platform-consistent rendering and the same drawing engine as native. The trade-off for observability tooling is that the semantic structure of the UI is not present in the DOM.
Why DOM recorders return a blank box
rrweb-style recorders (the engine behind PostHog, and conceptually similar to Clarity, Hotjar, FullStory, and LogRocket replay) walk the DOM, snapshot it, and stream subsequent mutations. Open your browser inspector on a CanvasKit app and you can see the problem yourself: under the host element sits a <flt-glass-pane> and a <canvas>, and that's essentially it. The recorder snapshots that one opaque element and has no DOM mutations to stream.
Some tools fall back to capturing the canvas as video. This is possible but slow: pulling pixels out of a WebGL/GPU canvas means a synchronous readback from the GPU to a CPU-side image for every frame, which stalls the main thread, so the resulting capture lands at a very low frame rate rather than the 60fps the app actually renders.
Flutter does expose a semantics tree for accessibility. But it is sparse, tuned for screen readers rather than visual fidelity, and nowhere near enough to reconstruct what the user saw. Even tools that try to read it cannot produce a faithful replay.
There is also a privacy catch worth flagging. DOM-based tools mask sensitive fields by selecting the elements to redact (a CSS class, a selector, an attribute). On a Flutter canvas there are no such elements, so even when one of these tools does manage a low-framerate video capture of the canvas, none of them can mask anything inside it. Every tool in this space documents that canvas content cannot be masked. Anything Flutter painted (passwords, payment details, personal data) is recorded in the clear, which is a real concern for GDPR, HIPAA, PCI, and similar obligations.
The frame-capture approach
The approach that works on CanvasKit is to capture the rendered frames directly, the same pixels Skia draws, and encode them efficiently off the main thread, with privacy masking applied at capture time before anything leaves the device.
That capture-time masking is also what closes the canvas privacy gap: because Pixeltrace works inside Flutter, you mark sensitive widgets and their regions are redacted before the frame is stored, which is exactly what DOM-based redaction cannot reach on a Flutter canvas.
Pixeltrace is built around exactly this model for Flutter web. It's in development; join the waitlist to be notified when it's available.