fix: align frozen frame threshold with Android Vitals (700ms) - #142
Conversation
Raise the default frozen frame threshold from 100ms to 700ms to match Android Vitals and OpenTelemetry Android slow rendering instrumentation. Stop emitting duplicate app_frozen_frame measurements on Android by reporting slow and frozen frames through polling only. Co-authored-by: Cursor <cursoragent@cursor.com>
aranhave
left a comment
There was a problem hiding this comment.
Nice update! 🔥 Just left a few small questions.
| - Filters out noise and reports user-perceptible jank | ||
| - **Frozen Frame Detection**: Individual frames exceeding threshold | ||
| - Default: 100ms | ||
| - Default: 700ms |
There was a problem hiding this comment.
Should we update the example below too? It shows two frozen frames with frozen_duration: 450.5, which isn't possible once each frozen frame must exceed 700ms.
There was a problem hiding this comment.
Fixed in bfdb40c: the invalid frozen_duration: 450.5 example was removed along with the docs/mobile-rum/ comparison docs.
Snapshot frozen count and duration atomically on Android/iOS, add polling regression tests, extract default frame constants, remove cross-SDK comparison docs, and drop Flutter comparison comments from the RN SDK. Co-authored-by: Cursor <cursoragent@cursor.com>
Document that frozen/slow frame monitoring guidance will live in frontend-o11y-knowledge-workbench once that epic is complete. Co-authored-by: Cursor <cursoragent@cursor.com>
Break the cross-SDK documentation line to satisfy the 80-character markdownlint limit in CI. Co-authored-by: Cursor <cursoragent@cursor.com>
Re-add the Documentation section accidentally dropped in the markdownlint README fix commit. Co-authored-by: Cursor <cursoragent@cursor.com>
…e-threshold-with-android-vitals # Conflicts: # docs/mobile-rum/feature-parity-matrix.md # docs/mobile-rum/index.md
aranhave
left a comment
There was a problem hiding this comment.
Thanks for working through the earlier feedback. I found three things that still need a look: the frozen-metric race, the demo timing and the public docs move.
Record and snapshot frozen count/duration under the same lock on Android and iOS, update demo heavy-load freezes to 750ms, and document frame monitoring in the public package README instead of removed comparison docs. Co-authored-by: Cursor <cursoragent@cursor.com>
aranhave
left a comment
There was a problem hiding this comment.
Thanks for working through these. The locking, demo timing, and public RN docs are all addressed now :) . LGTM 👍
Summary
The SDK previously treated any frame taking longer than 100ms as frozen. Industry tooling — including Android Vitals, OpenTelemetry Android slow rendering instrumentation, and common RUM competitors — uses 700ms as the frozen frame threshold (between slow jank at ~16ms and ANR at ~5s).
This PR aligns React Native frame monitoring with that standard and fixes duplicate reporting on Android.
Frozen frame threshold: 100ms → 700ms
Updated the default
frozenFrameThresholdMsacross the full stack:FrameMonitoringInstrumentation)FrameMonitor,FaroReactNativeModule)RefreshRateVitals,FaroReactNativeModule)The threshold remains configurable via
frameMonitoringOptions.frozenFrameThresholdMs.Effect: frames between 100ms and 700ms are no longer reported as
app_frozen_frame. They may still appear as slow frame events inapp_frames_ratewhen they meet the slow-frame grouping rules.Android: polling-only reporting for frozen/slow frames
On Android, frozen frames were previously reported twice:
onFrozenFramenative eventgetFrameMetrics()Slow frames already avoided this — native emitted
onSlowFrames, but JS never listened and only polled.This PR makes frozen frames consistent with slow frames and iOS: native accumulates metrics; JS polls every
refreshRatePollingInterval(default 30s) and emitsapp_frozen_frameonly whenfrozenFrames > 0. No zero-count payloads are sent.Android refresh rate events (
onRefreshRate) are unchanged.Documentation
Updated mobile RUM docs and demo copy to reflect the 700ms default and polling-based Android reporting.
Made with Cursor