Skip to content

fix: align frozen frame threshold with Android Vitals (700ms) - #142

Merged
yduartep merged 7 commits into
mainfrom
fix/align-frozen-frame-threshold-with-android-vitals
Aug 14, 2026
Merged

yduartep merged 7 commits into
mainfrom
fix/align-frozen-frame-threshold-with-android-vitals

Conversation

@yduartep

Copy link
Copy Markdown
Contributor

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 frozenFrameThresholdMs across the full stack:

  • TypeScript (FrameMonitoringInstrumentation)
  • Android native (FrameMonitor, FaroReactNativeModule)
  • iOS native (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 in app_frames_rate when they meet the slow-frame grouping rules.

Android: polling-only reporting for frozen/slow frames

On Android, frozen frames were previously reported twice:

  1. Immediately via the onFrozenFrame native event
  2. Again on the next 30s poll via getFrameMetrics()

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 emits app_frozen_frame only when frozenFrames > 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

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>
@yduartep
yduartep requested a review from a team as a code owner July 30, 2026 11:09

@aranhave aranhave left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice update! 🔥 Just left a few small questions.

Comment thread docs/mobile-rum/index.md Outdated
Comment thread docs/mobile-rum/index.md Outdated
- Filters out noise and reports user-perceptible jank
- **Frozen Frame Detection**: Individual frames exceeding threshold
- Default: 100ms
- Default: 700ms

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bfdb40c: the invalid frozen_duration: 450.5 example was removed along with the docs/mobile-rum/ comparison docs.

Comment thread docs/mobile-rum/index.md Outdated
Comment thread packages/react-native/src/instrumentations/frameMonitoring/instrumentation.ts Outdated
yduartep and others added 2 commits July 31, 2026 16:12
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>
yduartep and others added 3 commits July 31, 2026 16:25
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 aranhave left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread demo/src/screens/CrashDemoScreen.tsx
Comment thread README.md
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>
@yduartep
yduartep requested a review from aranhave August 1, 2026 18:56

@aranhave aranhave left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working through these. The locking, demo timing, and public RN docs are all addressed now :) . LGTM 👍

@yduartep
yduartep merged commit a22eabd into main Aug 14, 2026
18 checks passed
@yduartep
yduartep deleted the fix/align-frozen-frame-threshold-with-android-vitals branch August 14, 2026 06:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants