This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
mobilecritical PriorityID: compose-skiko-vsync-frame-pacing-guardrail

Compose Multiplatform Skiko Render Loop and VSync Frame-Pacing Standard

Enforces deterministic frame pacing and native VSync synchronization within Compose Multiplatform (CMP) and Skiko rendering pipelines on variable refresh rate (VRR / 120Hz ProMotion) displays. Prevents micro-stutter, frame drops, and pipeline stalling by binding Skiko frame invalidations directly to platform-native display links rather than unconstrained coroutine clocks or timer loops.

Prohibited Anti-Pattern: Unbounded coroutine-driven redraws and decoupled display link ticking causing 120Hz frame stutter and thermal throttling

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Compose Multiplatform Skiko Render Loop and VSync Frame-Pacing Standard - Enforces deterministic frame pacing and native VSync synchronization within Compose Multiplatform (CMP) and Skiko rendering pipelines on variable refresh rate (VRR / 120Hz ProMotion) displays. Prevents micro-stutter, frame drops, and pipeline stalling by binding Skiko frame invalidations directly to platform-native display links rather than unconstrained coroutine clocks or timer loops.
globs: **/src/commonMain/**/*.kt, **/src/iosMain/**/*.kt, **/src/androidMain/**/*.kt, **/src/jvmMain/**/*.kt
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/compose-skiko-vsync-frame-pacing-guardrail"
---

# Compose Multiplatform Skiko Render Loop and VSync Frame-Pacing Standard

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Unbounded coroutine-driven redraws and decoupled display link ticking causing 120Hz frame stutter and thermal throttling

## System Constraints
You are a Principal Mobile Architect enforcing frame pacing and render-loop invariants for Compose Multiplatform using Skiko. You must mandate strict synchronization with platform-native VSync (CADisplayLink on iOS, Choreographer on Android), explicit configuration of variable refresh rate ranges (up to 120Hz), and total prohibition of unconstrained redraw loops or thread-blocking rasterization.

## Strict Guidelines
- Engineers MUST ALWAYS synchronize Skiko rendering cycles directly to platform VSync mechanisms (CADisplayLink on iOS/macOS, Choreographer on Android) rather than detached coroutine clocks or arbitrary delay timers.
- Engineers MUST NOT call `needRedraw()` or manual invalidate triggers continuously inside hot recomposition paths or asynchronous coroutines without latching to the platform frame token.
- iOS targets MUST explicitly configure `CADisplayLink.preferredFrameRateRange` with matching minimum, preferred, and maximum rates (e.g., 60-120-120) when rendering high-cadence interactions on ProMotion displays.
- Android targets MUST utilize `Choreographer.postFrameCallback` or native `Choreographer` frame pacing via `MonotonicFrameClock` to avoid dual-loop phase drift between Skiko and the SurfaceView/HardwareRenderer.
- Render pipelines MUST NEVER allocate or re-instantiate `Skia.Surface`, `Skia.Canvas`, or Skiko render targets inside the per-frame draw loop; reusable buffers and render delegates MUST be cached and retained across cycles.
- Components NEVER execute blocking I/O, heavy JSON parsing, or long-running computations on the Skiko UI/Raster thread during frame delivery intervals.

## Bad vs Good Implementations
### Configuring Skiko iOS Frame Pacing for 120Hz ProMotion vs Unthrottled Ticking

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Attempting to pace frames using coroutine delay loops completely bypasses the iOS display server (QuartzCore/Metal). It guarantees out-of-phase swaps, micro-stutter, wasted GPU cycles, and thermal throttling on ProMotion devices.
```kotlin
// BAD: Spawning an unconstrained coroutine loop to trigger Skiko redraws
// Causes severe frame desync, skipped frames, and high thermal throttling
class CustomSkikoViewController : UIViewController() {
    private val scope = CoroutineScope(Dispatchers.Main)
    private lateinit var skikoView: SkikoView

    override fun viewDidLoad() {
        super.viewDidLoad()
        skikoView = SkikoView()
        view.addSubview(skikoView)

        scope.launch {
            while (isActive) {
                skikoView.needRedraw()
                delay(8) // Hardcoded 8ms (~120Hz) polling desynced from CADisplayLink
            }
        }
    }
}
```

#### Production Standard (Correct)
> **Standard**: Binds redraw requests directly to QuartzCore's CADisplayLink inside the common run loop and explicitly declares CAFrameRateRange (60-120-120), guaranteeing that Skiko swaps buffers precisely in phase with hardware rasterization.
```kotlin
// GOOD: Binding render invalidations directly to CADisplayLink with explicit ProMotion frame rate ranges
import platform.QuartzCore.CADisplayLink
import platform.QuartzCore.CAFrameRateRangeMake
import platform.Foundation.NSRunLoop
import platform.Foundation.NSRunLoopCommonModes

class CustomSkikoViewController : UIViewController() {
    private var displayLink: CADisplayLink? = null
    private lateinit var skikoView: SkikoView

    override fun viewDidLoad() {
        super.viewDidLoad()
        skikoView = SkikoView()
        view.addSubview(skikoView)
        setupVSyncPacing()
    }

    private fun setupVSyncPacing() {
        val target = DisplayLinkTarget { skikoView.needRedraw() }
        displayLink = CADisplayLink.displayLinkWithTarget(target, sel_registerName("onVsync:")).apply {
            // Explicitly request 120Hz dynamic pacing on ProMotion displays
            preferredFrameRateRange = CAFrameRateRangeMake(
                minimum = 60.0f,
                preferred = 120.0f,
                maximum = 120.0f
            )
            addToRunLoop(NSRunLoop.mainRunLoop, NSRunLoopCommonModes)
        }
    }

    override fun viewWillDisappear(animated: Boolean) {
        super.viewWillDisappear(animated)
        displayLink?.invalidate()
        displayLink = null
    }
}
```

Architectural Specification & Invariant Rationale

Architectural Invariant Design: Compose Multiplatform Skiko Render Loop and VSync Frame-Pacing Standard

Compose Multiplatform Skiko Render Loop and VSync Frame-Pacing Standard defines an industrial-grade behavioral standard for mobile engineering. Enforces deterministic frame pacing and native VSync synchronization within Compose Multiplatform (CMP) and Skiko rendering pipelines on variable refresh rate (VRR / 120Hz ProMotion) displays. Prevents micro-stutter, frame drops, and pipeline stalling by binding Skiko frame invalidations directly to platform-native display links rather than unconstrained coroutine clocks or timer loops. Modern software architectures require strict adherence to deterministic patterns to guarantee high availability, maintainability, and zero security vulnerabilities.

Framework Paradigm Shift & Drift MechanicsLLMs are trained on millions of open-source repositories containing outdated code, legacy idioms, and unpatched antipatterns. When coding assistants generate implementations for this stack, they default to common statistical tokens: Unbounded coroutine-driven redraws and decoupled display link ticking causing 120Hz frame stutter and thermal throttling Without an explicit system instruction file, developers spend hours debugging silent failures.
Failure Modes & Production Blast Radius

Hazard Analysis: What Breaks Without This Rule

The following failure modes have been cataloged from hundreds of hours of production code reviews where AI assistants produced non-compliant implementations:

Failure ModeAI Symptom (The Anti-Pattern)SeverityProduction Impact & Remedy
AI Hallucination & Deprecation DriftUnbounded coroutine-driven redraws and decoupled display link ticking causing 120Hz frame stutter and thermal throttlingCritical
Violating this industrial standard introduces regression bugs, security risks, and unexpected runtime failures in production.
Enforce explicit behavioral system directives across all AI coding assistants in your repository.
Silent Architecture InconsistencyAI assistants produce mixed paradigms across files, creating fragmented codebase conventions.Medium
Increases cognitive load during code reviews and leads to maintenance debt across engineering teams.
Pin project conventions in .cursor/rules, CLAUDE.md, and AGENTS.md so all team agents follow identical guardrails.
Architectural Directives in Detail

Step-by-Step Production Directives

Each rule constraint below is injected into the AI agent's system prompt to establish deterministic boundaries:

1

Engineers MUST ALWAYS synchronize Skiko rendering cycles directly to platform VSync mechanisms (CADisplayLink on iOS/macOS, Choreographer on Android) rather than detached coroutine clocks or arbitrary delay timers.

Engineers MUST ALWAYS synchronize Skiko rendering cycles directly to platform VSync mechanisms (CADisplayLink on iOS/macOS, Choreographer on Android) rather than detached coroutine clocks or arbitrary delay timers.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
2

Engineers MUST NOT call `needRedraw()` or manual invalidate triggers continuously inside hot recomposition paths or asynchronous coroutines without latching to the platform frame token.

Engineers MUST NOT call `needRedraw()` or manual invalidate triggers continuously inside hot recomposition paths or asynchronous coroutines without latching to the platform frame token.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
3

iOS targets MUST explicitly configure `CADisplayLink.preferredFrameRateRange` with matching minimum, preferred, and maximum rates (e.g., 60-120-120) when rendering high-cadence interactions on ProMotion displays.

iOS targets MUST explicitly configure `CADisplayLink.preferredFrameRateRange` with matching minimum, preferred, and maximum rates (e.g., 60-120-120) when rendering high-cadence interactions on ProMotion displays.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
4

Android targets MUST utilize `Choreographer.postFrameCallback` or native `Choreographer` frame pacing via `MonotonicFrameClock` to avoid dual-loop phase drift between Skiko and the SurfaceView/HardwareRenderer.

Android targets MUST utilize `Choreographer.postFrameCallback` or native `Choreographer` frame pacing via `MonotonicFrameClock` to avoid dual-loop phase drift between Skiko and the SurfaceView/HardwareRenderer.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
5

Render pipelines MUST NEVER allocate or re-instantiate `Skia.Surface`, `Skia.Canvas`, or Skiko render targets inside the per-frame draw loop

Render pipelines MUST NEVER allocate or re-instantiate `Skia.Surface`, `Skia.Canvas`, or Skiko render targets inside the per-frame draw loop; reusable buffers and render delegates MUST be cached and retained across cycles.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
6

Components NEVER execute blocking I/O, heavy JSON parsing, or long-running computations on the Skiko UI/Raster thread during frame delivery intervals.

Components NEVER execute blocking I/O, heavy JSON parsing, or long-running computations on the Skiko UI/Raster thread during frame delivery intervals.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
Technical Questions & Answers

Frequently Asked Technical Questions

Common questions developers face when configuring AI coding assistants for this stack:

Q:Why is Compose Multiplatform Skiko Render Loop and VSync Frame-Pacing Standard critical for AI coding assistants?

Enforces deterministic frame pacing and native VSync synchronization within Compose Multiplatform (CMP) and Skiko rendering pipelines on variable refresh rate (VRR / 120Hz ProMotion) displays. Prevents micro-stutter, frame drops, and pipeline stalling by binding Skiko frame invalidations directly to platform-native display links rather than unconstrained coroutine clocks or timer loops. Without explicit behavioral boundaries, AI coding assistants (Cursor, Claude Code, Windsurf, Codex) rely on training data averages, which frequently produce deprecated, inefficient, or vulnerable code patterns.

Q:How does adopting this rule prevent AI hallucinations?

This rule explicitly declares what the AI must avoid (Unbounded coroutine-driven redraws and decoupled display link ticking causing 120Hz frame stutter and thermal throttling) and prescribes verified positive examples. Grounding the LLM with negative and positive pairs eliminates guesswork.

Q:Which AI coding environments are supported?

This specification is multi-platform compatible: Cursor (.cursor/rules/*.mdc & .cursorrules), Anthropic Claude Code (CLAUDE.md), Windsurf (.windsurfrules), Codex / Cline (AGENTS.md), and GitHub Copilot (.github/copilot-instructions.md).

Q:Can I combine this rule with other technology stacks?

Yes. Use the Interactive Stack Builder or download an Agent Pack to merge this rule with other frontend, backend, database, security, and DevOps rules without syntax collision.

Open Specification · CC BY 4.0·Free for Personal, Team & Commercial In-Repo Use

Citation & Open Source Attribution Guidelines

This specification is licensed under Creative Commons Attribution 4.0 International (CC BY 4.0). You are completely free to adapt and share this rule in your workspace. If citing, including in public AI benchmark datasets, or redistributing, attribution to AgentRules.org is required.

Related MOBILE Rules

View all rules →
1 Rule in Stack

Compose