惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 聂微东
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
L
LangChain Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 司徒正美
WordPress大学
WordPress大学
T
The Blog of Author Tim Ferriss
Blog — PlanetScale
Blog — PlanetScale
J
Java Code Geeks
Y
Y Combinator Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
GbyAI
GbyAI
Vercel News
Vercel News
大猫的无限游戏
大猫的无限游戏
T
Tailwind CSS Blog
Jina AI
Jina AI
B
Blog
Recorded Future
Recorded Future
MyScale Blog
MyScale Blog
I
InfoQ
aimingoo的专栏
aimingoo的专栏
博客园_首页
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Cloudflare Blog
雷峰网
雷峰网
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
腾讯CDC
爱范儿
爱范儿
Last Week in AI
Last Week in AI
博客园 - 三生石上(FineUI控件)
博客园 - Franky
Schneier on Security
Schneier on Security
V
V2EX
TaoSecurity Blog
TaoSecurity Blog
H
Hacker News: Front Page
Cloudbric
Cloudbric
D
DataBreaches.Net
B
Blog RSS Feed
P
Palo Alto Networks Blog
云风的 BLOG
云风的 BLOG
NISL@THU
NISL@THU
I
Intezer
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Cyberwarzone
Cyberwarzone
F
Fortinet All Blogs
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Cybersecurity and Infrastructure Security Agency CISA
C
Cisco Blogs
K
Kaspersky official blog
Forbes - Security
Forbes - Security

Amplitude

What Makes a Good vs Bad North Star Metric The Role of Feature Management in Successful Product Development Cohort Retention Analysis: Reduce Churn Using Customer Data 7 Steps to Measuring the Success of a Feature 14 Best Product Management Tools for 2026 (Plus Tips from Senior PMs) Putting A Number On AI Quality Meet the Winners of the 2026 Amplitude AI Impact Awards Beyond Last-Touch Attribution: Find Out Which Interactions Really Matter Agent Connectors Are Better Together Agents That Act on What Actually Happened How Square Used Amplitude to Enhance the Seller Experience and Power Growth Migrating Analytics Platforms Without The Chaos Wanted Lab Grows Sign-Ups by 150% & Builds Experimentation Culture How to Balance Inference Cost and User Experience for Agents Introducing Zoning Insights: Web Intelligence at a Glance Five best practices for getting started with AI agents 24 Quarters at #1. Here’s What’s Next. How We Built a Product That Tells Us What To Build Next: Inside Amplitude Wave Looking Beyond Campaign Metrics: 7 Marketing Success Stories AI Evals for Product Managers: A Beginner’s Guide to Getting Started The Builder Skills Library Introducing Agent Connectors in Amplitude Understand How AI Thinks, Get Better Results How We Redesigned Amplitude Docs for Agents and Made Everyone an Author AI Broke Your Experimentation Program. Here’s How to Fix It. Every Stuck User Is a Support Ticket Waiting to Happen Tracing the Sale: Connect Behavior to Conversions with Persisted Properties Building CLI Agents: It’s What You Don’t Give Them That Counts Three Tips for Better Prompts in Amplitude Global Agent How AI Took the Data Analyst’s Job, and Created a Better One Default Prompts Are Tanking Your Agent’s Retention Optimizing Core Web Vitals with Amplitude’s Global Agent Don’t Ask Global Agent Anything, Ask These Three Things How We Built a Design Agent at Amplitude with Claude Managed Agents and Cloudflare The Problem with Chasing Churn How Hostinger Achieved a 20%+ Conversion Lift Through Experimentation How STAGE Streams Smarter by Putting Data at the Center Building the Validation Stack for AI Product Development Making AI Analytics Safe for Financial Services Teams Amplitude Heatmaps Update: More Reliable Screenshots and Accurate Placement Most Teams Ship Agent Personalities by Accident. We Didn’t. What I Learned Pointing a Ralph Loop at My Product for a Week How Mercado Libre Scales Decision Making with AI Claude Cowork for PMs: 5 Playbooks to Get Started How ACKO Drove 13% More Conversions & 50% Drop in Calls with GenAI Agents Just Made Your Feature Launch Channel Smarter Homegrown FinOps Tools: How AI “Build” Beat “Buy” for Us in <1 Year Introducing The Amplitude Quickstart Series The Eval Signal That Predicts 3x Agent Retention Agents Write Code. Fixing It Is Still On You. Amplitude and Statsig Partnership 5 Agent Skills to Automate Your Weekly Product Review Amplitude Plug and Play: New AI Plugin in Claude and Cursor Marketplaces Introducing Amplitude Wizard CLI: Set Up Amplitude from Your Codebase Making AI Search Count (and Convert) How VEED Evolved Its AI Search Strategy What’s New with Amplitude Agents Effortless Support at Scale: Making Human Support More Human AI Week 2026: Upleveling All Together Amplitude AI Builders: Paul Hultgren Chats about AI Assistant Dashboard Dread to AI-Driven Decisions: How Tira Rebuilt Its Analytics Workflow Your Product Deserves a Better Support Agent How Cisco Systems Accelerated Adoption by 20% Through Data Innovation
Rebuilding Session Replay’s Delivery Layer to Be Lighter on Your Page
Lew Gordon · 2026-05-07 · via Amplitude

Session Replay reconstructs every user session from two ingredients: a snapshot of the DOM and a stream of incremental events. For that to work, every event has to make it to the server, and they have to arrive in the order they happened. The browser’s page lifecycle makes the first one harder than it sounds. Idle scheduling makes the second a subtle bug.

We fixed both: Replay data now arrives more complete, payloads are down up to 36% on DOM-heavy sessions, and compression work has been moved entirely off your main thread. Here’s what we changed, and what we learned along the way.

The challenges we took on

Getting the last events out the door

The browser’s page lifecycle creates a challenge for any network-based SDK. When a user navigates away or closes a tab, the browser tears down the page, taking pending network requests along with it. That means for sessions that end with a fast navigation (e.g., a click on a link or back button), the final batch never ships.

This is a well-understood constraint of how browsers work, not something you can route around with a smarter retry strategy. You need a fundamentally different delivery primitive for the page-unload case.

Sending events in the right order

Session Replay reconstructs a user’s session from two ingredients: a full snapshot of the DOM at the start of the session (or after a re-render), and a sequence of incremental events (mutations, mouse moves, clicks) that describe how the DOM changed over time.

Getting these to the server in strict order matters. An incremental event like “the text of node #42 changed to ‘Hello’” is only meaningful if the player already knows what node #42 is, which it learns from the preceding snapshot. The replay player reconstructs the state forward from the snapshot; it can’t resolve a reference to a node it hasn’t seen yet.

The interaction between idle-time event processing and full snapshot triggers creates a subtle scheduling challenge: Under main-thread load, incremental events deferred to the idle queue could be in flight when a new snapshot is triggered. If those batches arrived out of order, the player would encounter references to nodes it hadn’t seen yet.

What we built

Draining the queue before every snapshot

The fix for the ordering issue was straightforward once the mechanism was clear: Drain the idle queue synchronously before processing any full snapshot. When a snapshot is triggered, we first flush every pending event, preserving their order relative to the snapshot, then send the snapshot itself. The server now always receives events in the order they happened, with no dependency on network timing.

We kept requestIdleCallback for incremental events. The browser schedules that compression work during idle windows between user interactions, so Session Replay’s processing overhead doesn’t compete with your UI for main-thread time.

A smarter payload format

One of the more impactful changes is how we encode events before sending them.

The original format compressed each rrweb event individually with zlib and latin1-encoded it into a string, then packed those compressed strings into a JSON array. The intention was to keep each event small. The tradeoff is that per-event compression can’t see patterns that span events.

Gzip finds storage savings by identifying repeated byte sequences across a block of data. A DOM capture has a lot of repetition: The same node IDs, attribute names, and CSS class strings appear across hundreds of consecutive events. Compressing each event in isolation makes those cross-event patterns invisible to the compressor.

We now serialize events as plain JSON, pack them together, and apply a single gzip pass over the entire batch. The compressor sees the full context and exploits all the repetition that spans events.

We benchmarked this against 20 sessions from a single production application. For most sessions, wire size dropped around 22–24%, with DOM-heavy sessions reaching up to 36% (the full spread across percentiles was 22.4–36.5%). The variation reflects how much event repetition matters: sessions with heavier, more uniform DOM activity give the compressor more to work with. Results will vary by application, but the direction is consistent.

Native compression, off the main thread

Compression reduces payload sizes significantly. But it also consumes CPU, and running it synchronously on the main thread trades one problem for another. We solve both at once.

Compression runs inside a Web Worker, a background thread with no access to the main thread’s event loop. And instead of a JavaScript compression library, we use the browser's native CompressionStream API.

CompressionStream is implemented as native compiled code inside the browser engine, using the same infrastructure the browser uses to decompress web responses. Compare this to a JavaScript library like fflate, and you’ll find:

  • No bundle size cost. The compressor is already in the browser. There’s nothing to ship.
  • Native-speed execution. CompressionStream runs outside the JS heap entirely, rather than interpreted JavaScript.

The main thread hands off a raw event string and moves on. Compression, batching, HTTP delivery, and retry logic all happen in the background, invisible to the page.

Ensuring data durability during teardown

IndexedDB is Session Replay’s primary durability layer. Events are written to IndexedDB as they’re captured, and the SDK delivers them via fetch in batches throughout the session. The goal is to have everything shipped before the user ever leaves the page.

For the events that haven’t made it out when the page tears down, we now also use navigator.sendBeacon. Unlike fetch, sendBeacon is fire-and-forget: The browser accepts the payload and delivers it even after the page has been torn down. It was designed specifically for this case.

sendBeacon has a payload size limit (typically 64 KB per browser). When pending events exceed that limit, we trim: the SDK keeps the oldest events and drops the newest ones to fit. The choice of oldest-first is a structural requirement of how replay works, not a product tradeoff: the player reconstructs sessions by starting from a full snapshot and replaying events forward. Dropping the oldest events would sever the connection to that starting snapshot and make the remaining events unplayable. In practice, the sendBeacon path is rarely carrying much load; the more that ships via fetch during the session, the less the safety net needs to cover.

What changed for your replays

Sessions that end with fast navigations now consistently include their final event batches. The snapshot ordering issue has been structurally resolved by the queue-drain guarantee. Median payloads are smaller (up to 36% smaller on DOM-heavy sessions) with no change to what gets captured and no bundle-size cost.

What’s next

Delivery reliability and efficiency are only half the picture. The next area we’re looking at is observability. The idea is to enable you to see when events were trimmed, IndexedDB writes encounter capacity limits, or the sendBeacon path carries more than it should. And when we do it, we’ll write that up. Expect to hear more soon!

A note on browser support: CompressionStream is available in Chrome 80+, Firefox 113+, and Safari 16.4+. On older browsers, Session Replay sends uncompressed JSON, which is roughly comparable in size to the old per-event compressed format. There’s no behavioral difference, just no compression savings on those browsers.

Availability

These improvements shipped in @amplitude/session-replay-browser@1.42.0 and @amplitude/plugin-session-replay-browser@1.30.0. If you’re on an older version, update your SDK to get them automatically.

The idle-time processing, payload format change, and sendBeacon improvements are on

by default. To also enable the Web Worker path for compression and HTTP delivery:

1. amplitude.add(sessionReplayPlugin({
2. useWebWorker: true,
3. }))
;

The Web Worker option isn’t the default setting because it changes how compression and delivery work under the hood, and we didn’t want to silently alter behavior for existing integrations. If you’re starting fresh or have confirmed that your application works as expected with the flag enabled, it’s the recommended configuration.