agentsclimarketplace

Frame rate optimization

Skill almasumdev/awesome-mobile-performance-agent-skills/.github/skills/rendering/frame-rate-optimization

Agent skills for profiling and optimizing mobile app performance (startup, memory, frame-rate, network).

Install
npx -y skills add almasumdev/awesome-mobile-performance-agent-skills --skill frame-rate-optimization

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Hit 60/90/120Hz frame rate budgets by balancing CPU (UI thread) and GPU (RenderThread/Metal) work. Use when frame time exceeds the device refresh budget.

SKILL.md

5.2 KB, as published. Nobody here has run it

Frame Rate Optimization

Instructions

A smooth UI is not about doing less; it is about doing work within the frame budget and not blocking the pipeline. This skill covers the mental model and the fixes that apply across iOS, Android, Flutter, and React Native.

1. Know the Budget

Refresh rateFrame budgetSlack after 4ms overhead
60 Hz16.67 ms~12 ms
90 Hz11.11 ms~7 ms
120 Hz8.33 ms~4 ms

Work on two threads must each fit in the budget:

  • UI / main thread — layout, measure, produce a display list.
  • Render thread / GPU — rasterize, composite, present.

2. Pipeline by Platform

  • Android: Choreographer.doFrameView.measure/layout/drawRenderThreadSurfaceFlinger.
  • iOS: Run loop → CALayer commit → CARenderServer → GPU → display. Core Animation always runs on a dedicated thread.
  • Flutter: Dart UI thread (widget → element → render object) → Skia/Impeller raster thread → GPU.
  • React Native: JS thread → shadow thread (Yoga layout) → UI thread / Fabric → RenderThread.

If either thread exceeds the budget, a frame is dropped. Traces usually show which.

3. UI-Thread Wins

  • Flatten view hierarchies. In Android avoid nested LinearLayout; use ConstraintLayout or Compose. In iOS flatten UIStackView nesting beyond 3 levels.
  • Cache expensive measurements. In SwiftUI avoid GeometryReader in list rows; prefer fixedSize() or ViewThatFits. In Compose hoist remember { } for computed text styles.
  • Avoid per-frame allocation. In Flutter use const widgets. In RN memoize row components (React.memo, stable keys).

Compose example — stable lambdas:

@Composable
fun FeedRow(item: Item, onClick: (Item) -> Unit) {
    // onClick captured as stable - prefer passing item id rather than a new lambda per frame
    Row(Modifier.clickable { onClick(item) }) { /* ... */ }
}

SwiftUI — equatable views:

struct Row: View, Equatable {
    let item: Item
    var body: some View { HStack { Text(item.title) } }
    static func == (a: Row, b: Row) -> Bool { a.item.id == b.item.id }
}
List(items) { Row(item: $0).equatable() }

4. GPU / Raster-Thread Wins

  • Avoid large blurs. A full-screen Gaussian blur on a 1080p phone costs ~4-6 ms on the raster thread. Replace with a static snapshot or a smaller blurred layer.
  • Rasterize expensive static trees. Flutter: RepaintBoundary. Android View: setLayerType(LAYER_TYPE_HARDWARE). iOS: layer.shouldRasterize = true; layer.rasterizationScale = UIScreen.main.scale for static content only.
  • Prefer vector assets over large bitmaps for icons, but cap vector complexity. Extremely complex SVGs can beat large PNGs on CPU but cost more on GPU.
  • Use platform-native gradients. LinearGradient on GPU beats painting pixels in CPU.

5. Animations at High Refresh Rate

  • Drive animations from the platform compositor, not JS/Dart setState loops.
    • Flutter: AnimatedBuilder with a Ticker; avoid setState per frame at root.
    • RN: Animated with useNativeDriver: true, or Reanimated worklets.
    • Compose: animate*AsState, Animatable, or rememberInfiniteTransition — they run on the choreographer, not recomposition.
    • SwiftUI: withAnimation and TimelineView; Core Animation implicit animations when possible.

RN Reanimated worklet:

const offset = useSharedValue(0);
const style = useAnimatedStyle(() => ({ transform: [{ translateX: offset.value }] }));
offset.value = withSpring(100);

6. Target the Right Refresh Rate

  • iOS: request 120Hz with CADisplayLink.preferredFrameRateRange = CAFrameRateRange(minimum: 80, maximum: 120, preferred: 120). Without this, ProMotion falls back to 60Hz for UIKit animations.
  • Android: declare 120Hz support in manifest: <meta-data android:name="android.max_aspect" .../> is not enough; set Window.attributes.preferredDisplayModeId or use Surface.setFrameRate() on Android 11+.
  • Flutter: GestureBinding.instance.resamplingEnabled = true helps touch-driven 120Hz.

7. Verify

adb shell dumpsys gfxinfo com.example.app framestats prints a histogram of frame times. Expect the 95th-percentile bucket under the budget.

Instruments Core Animation FPS gauge or UICoreAnimationFramesPerSecond metric from MetricKit can be wired into CI.

Flutter DevTools Performance shows UI and Raster thread stacks side by side for every frame.

Checklist

  • Frame budget documented per screen (60/90/120Hz target known).
  • View / widget hierarchy is ≤ 10 deep on hot screens.
  • No full-screen blur without measurement justifying it.
  • const / memoized rows and stable keys in lists.
  • Animations use platform compositors, not per-frame setState loops.
  • High-refresh-rate display modes requested on iOS and Android when appropriate.
  • gfxinfo framestats or Core Animation FPS captured and p95 under budget.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.