Platform

AI

Amplitude AI
Analytics that never stops working
AI Agents
Sense, decide, and act faster than ever before
AI Visibility
See how your brand shows up in AI search
AI Feedback
Distill what your customers say they want
Amplitude MCP
Insights from the comfort of your favorite AI tool
AI Assistant
Support powered by product data

Insights

Product Analytics
Understand the full user journey
Marketing Analytics
Get the metrics you need with one line of code
Session Replay
Visualize sessions based on events in your product
Heatmaps
Visualize clicks, scrolls, and engagement

Action

Guides and Surveys
Guide your users and collect feedback
Feature Experimentation
Innovate with personalized product experiences
Web Experimentation
Drive conversion with A/B testing powered by data
Feature Management
Build fast, target easily, and learn as you ship
Activation
Unite data across teams

Data

Data Governance
Complete data you can trust
Integrations
Connect Amplitude to hundreds of partners
Security & Privacy
Keep your data secure and compliant
Solutions
Solutions that drive business results
Deliver customer value and drive business outcomes
Amplitude Solutions →

Industry

Financial Services
Personalize the banking experience
B2B
Maximize product adoption
Media
Identify impactful content
Healthcare
Simplify the digital healthcare experience
Ecommerce
Optimize for transactions

Use Case

Acquisition
Get users hooked from day one
Retention
Understand your customers like no one else
Monetization
Turn behavior into business

Team

Product
Fuel faster growth
Data
Make trusted data accessible
Engineering
Ship faster, learn more
Marketing
Build customers for life
Executive
Power decisions, shape the future

Size

Startups
Free analytics tools for startups
Enterprise
Advanced analytics for scaling businesses
Resources

Learn

Blog
Thought leadership from industry experts
Resource Library
Expertise to guide your growth
Compare
See how we stack up against the competition
Glossary
Learn about analytics, product, and technical terms
Explore Hub
Detailed guides on product and web analytics

Connect

Community
Connect with peers in product analytics
Events
Register for live or virtual events
Customers
Discover why customers love Amplitude
Partners
Accelerate business value through our ecosystem

Support & Services

Customer Help Center
All support resources in one place: policies, customer portal, and request forms
Developer Hub
Integrate and instrument Amplitude
Academy & Training
Become an Amplitude pro
Customer Success
Drive business success with expert guidance and support
Product Updates
See what's new from Amplitude

Tools

Benchmarks
Understand how your product compares
Prompt Library
Prompts for Agents to get started
Templates
Kickstart your analysis with custom dashboard templates
Tracking Guides
Learn how to track events and metrics with Amplitude
Maturity Model
Learn more about our digital experience maturity model
Pricing
LoginContact salesGet started

AI

Amplitude AIAI AgentsAI VisibilityAI FeedbackAmplitude MCPAI Assistant

Insights

Product AnalyticsMarketing AnalyticsSession ReplayHeatmaps

Action

Guides and SurveysFeature ExperimentationWeb ExperimentationFeature ManagementActivation

Data

Data GovernanceIntegrationsSecurity & Privacy
Amplitude Solutions →

Industry

Financial ServicesB2BMediaHealthcareEcommerce

Use Case

AcquisitionRetentionMonetization

Team

ProductDataEngineeringMarketingExecutive

Size

StartupsEnterprise

Learn

BlogResource LibraryCompareGlossaryExplore Hub

Connect

CommunityEventsCustomersPartners

Support & Services

Customer Help CenterDeveloper HubAcademy & TrainingCustomer SuccessProduct Updates

Tools

BenchmarksPrompt LibraryTemplatesTracking GuidesMaturity Model
LoginSign Up

Rebuilding Session Replay’s Delivery Layer to Be Lighter on Your Page

How we made Session Replay more reliable and efficient
Product

May 7, 2026

9 min read

Lew Gordon

Lew Gordon

Senior Staff Software Engineer, Amplitude

The heading of Session Replay updates on a blue field

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.


About the author
Lew Gordon

Lew Gordon

Senior Staff Software Engineer, Amplitude

More from Lew

Lew Gordon is a Senior Staff Engineer at Amplitude where he works on Session Replay. He was formerly an engineer at Twilio.

More from Lew
Topics

Amplitude Session Replay

Data

Engineering

Recommended Reading

article card image
Read 
Insights
The Eval Signal That Predicts 3x Agent Retention

May 7, 2026

6 min read

article card image
Read 
Product
Agents Write Code. Fixing It Is Still On You.

May 6, 2026

6 min read

article card image
Read 
Company
Amplitude and Statsig partnership

May 5, 2026

2 min read

article card image
Read 
Insights
5 Agent Skills to Automate Your Weekly Product Review

May 4, 2026

6 min read

Platform
  • AI Agents
  • AI Visibility
  • AI Feedback
  • Amplitude MCP
  • AI Assistant
  • Product Analytics
  • Web Analytics
  • Feature Experimentation
  • Feature Management
  • Web Experimentation
  • Session Replay
  • Guides and Surveys
  • Activation
Compare us
  • Adobe
  • Google Analytics
  • Contentsquare
  • Fullstory
  • Heap
  • LaunchDarkly
  • Mixpanel
  • Optimizely
  • Pendo
  • PostHog
Resources
  • Resource Library
  • Blog
  • Agent Prompt Library
  • Product Updates
  • Amp Champs
  • Amplitude Academy
  • Events
  • Glossary
Partners & Support
  • Status
  • Contact Us
  • Customer Help Center
  • Community
  • Developer Docs
  • Partner Program
  • Partner Directory
  • Become an affiliate
Company
  • About Us
  • Careers
  • Press & News
  • Investor Relations
  • Diversity, Equity & Inclusion
Terms of ServicePrivacy NoticeAcceptable Use PolicyLegal
EnglishJapanese (日本語)Korean (한국어)Español (LATAM)Español (Spain)Português (Brasil)Português (Portugal)FrançaisDeutsch
© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.
Blog
InsightsProductCompanyCustomers
Topics

101

AI

APJ

Acquisition

Adobe Analytics

Agents

Amplify

Amplitude AI

Amplitude Academy

Amplitude Activation

Amplitude Agent Analytics

Amplitude Analytics

Amplitude Audiences

Amplitude Community

Amplitude Feature Experimentation

Amplitude Full Platform

Amplitude Guides and Surveys

Amplitude Heatmaps

Amplitude Made Easy

Amplitude Session Replay

Amplitude Web Experimentation

Amplitude on Amplitude

Analytics

B2B SaaS

Behavioral Analytics

Benchmarks

Churn Analysis

Cohort Analysis

Collaboration

Consolidation

Conversion

Customer Experience

Customer Lifetime Value

Customer Support

DEI

Data

Data Governance

Data Management

Data Tables

Digital Experience Maturity

Digital Native

Digital Transformer

EMEA

Ecommerce

Employee Resource Group

Engagement

Engineering

Event Tracking

Experimentation

Feature Adoption

Financial Services

Funnel Analysis

Getting Started

Google Analytics

Growth

Healthcare

How I Amplitude

Implementation

Integration

LATAM

LLM

Life at Amplitude

MCP

Machine Learning

Marketing Analytics

Media and Entertainment

Metrics

Modern Data Series

Monetization

Next Gen Builders

North Star Metric

Partnerships

Personalization

Pioneer Awards

Privacy

Product 50

Product Analytics

Product Design

Product Management

Product Releases

Product Strategy

Product-Led Growth

Recap

Retention

Revenue

Startup

Tech Stack

The Ampys

Warehouse-native Amplitude