
Instruments @Composable functions with lightweight tracking to monitor recomposition rates against per-composable budgets, detect violations, and report via IDE performance cockpit, CLI, and logs.
Compose recomposition budget monitor — catch runaway recompositions before they ship.
Rebound is a Kotlin compiler plugin that instruments every @Composable function with lightweight tracking calls. At runtime, it monitors recomposition rates against per-composable budgets, detects violations, and reports them via an Android Studio tool window, CLI, or logcat. Works on Android and iOS (Compose Multiplatform). Zero config required — just apply the Gradle plugin. The IDE plugin provides a 5-tab performance cockpit with live monitoring, hot spots ranking, timeline heatmap, stability analysis, and session history with VCS correlation.
@ReboundBudget annotation — override the inferred budget class for any composablesnapshot, summary, watch, ping commands over ADB socket1. Apply the Gradle plugin
// settings.gradle.kts
pluginManagement {
repositories {
gradlePluginPortal()
mavenCentral()
}
}// build.gradle.kts (app module)
plugins {
id("io.github.aldefy.rebound") version "0.2.1"
}2. Configure (optional)
rebound {
enabled.set(true) // default: true
debugOnly.set(true) // default: true — only instruments debug builds
}3. Run your app
Rebound auto-installs on first composition. Violations appear in logcat:
W/Rebound: [VIOLATION] MyScreen — 8 recomp/s (budget: 3, class: SCREEN)
Connect the IDE plugin or CLI for richer output.
The compiler plugin injects ReboundTracker.onCompositionEnter/Exit calls into every @Composable function at the IR level. The runtime tracks rates per sliding window and reports violations. The IDE plugin and CLI connect via LocalServerSocket("rebound") forwarded through ADB (Android) or via a WebSocket relay (iOS physical devices).
Platform support: The compiler plugin instruments all KMP targets. Android uses
LocalServerSocket+ ADB forward. iOS simulator uses a direct TCP server on:18462. iOS physical devices connect outbound to a Mac-side WebSocket relay via Bonjour auto-discovery, with console logging as fallback.
| Module | Description |
|---|---|
rebound-runtime |
KMP runtime library (Android, iOS, JVM). Full transport on all platforms. |
rebound-compiler |
Kotlin compiler plugin for Kotlin 2.0.x-2.1.x |
rebound-compiler-k2 |
Kotlin compiler plugin for Kotlin 2.2+ |
rebound-gradle |
Gradle plugin — auto-wires compiler + runtime, selects correct artifact |
rebound-ide |
Android Studio plugin — 5-tab performance cockpit with editor integration |
tools/rebound-relay |
Mac relay server — bridges iOS physical devices to CLI/IDE via WebSocket |
sample |
Sample Android app |
rebound {
enabled.set(true) // Master switch — set false to disable all instrumentation
debugOnly.set(true) // Only instrument debug builds (release builds get no overhead)
}Runtime toggles:
ReboundTracker.enabled = true // Master on/off at runtime
ReboundTracker.logCompositions = false // Per-composition logcat (throttled 1/s per composable)Each composable is auto-classified by IR heuristics:
| Budget Class | Rate/sec | Heuristic |
|---|---|---|
SCREEN |
3 | Name contains Screen or Page
|
CONTAINER |
10 | Has child @Composable calls |
INTERACTIVE |
30 | Default for unclassified |
LIST_ITEM |
60 | Inside LazyColumn/LazyRow/LazyGrid
|
ANIMATED |
120 | Calls animate*/Transition/Animation APIs |
LEAF |
5 | No child @Composable calls |
Screen or Page.derivedStateOf or move reads into a smaller scope.@Composable calls — Column, Row, Box, Scaffold content slots.@Composable calls in its body.animate*, Transition, Animation, or Animatable APIs.@Composable calls — Text(), Icon(), Image().@Composable calls in the function body.| Color | Condition | Status |
|---|---|---|
| Red | rate > budget | OVER |
| Yellow | rate > 70% of budget | NEAR |
| Green | rate <= 70% of budget | OK |
| Gray | not actively recomposing | — |
| Interaction State | Multiplier | Effect |
|---|---|---|
| IDLE | 1x | Normal budgets |
| SCROLLING | 2x | Budgets doubled during scroll |
| ANIMATING | 1.5x | Budgets increased during animation |
| USER_INPUT | 1.5x | Budgets increased during input |
// This composable uses tilt sensor — it's not a leaf, it's animated
@ReboundBudget(BudgetClass.ANIMATED)
@Composable
fun TiltDrivenSticker(offset: Offset) { ... }The Android Studio plugin (targets 2024.2.1.3+) provides a 5-tab performance cockpit, editor integration, and session persistence. Configure via Preferences > Tools > Rebound.
Monitor — Live composable tree with sparkline rate history per node. Scrolling event log at the bottom shows recomposition events, violations, and state changes in real time.
Hot Spots — Sortable flat table of all tracked composables, ranked by severity (OVER > NEAR > OK). Summary card at the top shows violation/warning/OK counts at a glance. Click any row to jump to source.
Timeline — Composable x time heatmap. Each cell is colored green/yellow/red based on budget status at that moment. Scroll back up to 60 minutes. Useful for correlating recomposition spikes with user interactions.
Stability — Parameter stability matrix showing SAME/DIFFERENT/STATIC/UNCERTAIN status per parameter for each composable. Cascade impact tree visualizes how unstable parameters propagate recompositions through the hierarchy.
History — Saved sessions stored in .rebound/sessions/. Each session is VCS-tagged with branch name and commit hash. Side-by-side comparison view for before/after regression analysis.
@Composable function declarations, reflecting live budget status.> 12/s | budget: 8/s | OVER | skip: 45%.Rebound: 45 composables | 3 violations.The plugin connects via adb forward tcp:18462 localabstract:rebound. Install from the rebound-ide build output.
The CLI auto-detects the connection path: direct TCP (iOS simulator or relay), ADB forward (Android), or devicectl console (iOS physical without relay).
./rebound-cli.sh snapshot # Full JSON metrics for all tracked composables
./rebound-cli.sh summary # Top 10 composables by recomposition rate
./rebound-cli.sh watch # Live updates every 1 second
./rebound-cli.sh ping # Health check → "pong"Or query directly:
echo "snapshot" | nc localhost 18462For iOS physical devices, the runtime connects outbound to a Mac-side relay via WebSocket. The device discovers the relay automatically via Bonjour.
# Build the relay (one-time)
./tools/build-relay.sh
# Start the relay on your Mac
./tools/rebound-relay
# → TCP :18462 (CLI/IDE), WebSocket :18463 (devices), Bonjour: _rebound._tcp
# Now run your app on the physical device (same WiFi network)
# The device auto-discovers the relay and connects
./rebound-cli.sh snapshot # works transparently through relayOverride Bonjour discovery with an env var if needed (e.g., different subnet):
REBOUND_RELAY_HOST=192.168.1.100:18463
| Kotlin Version | Compiler Artifact | Status |
|---|---|---|
| 2.0.x | rebound-compiler |
Stable |
| 2.1.x | rebound-compiler |
Stable |
| 2.2.x | rebound-compiler-kotlin-2.2 |
Stable |
| 2.3.x | rebound-compiler-kotlin-2.3 |
Stable |
The Gradle plugin auto-detects your project's Kotlin version and selects the correct compiler artifact. No manual configuration needed.
| Platform | Transport | Status |
|---|---|---|
| Android (device/emulator) |
LocalServerSocket + ADB forward |
Stable |
| iOS Simulator | Direct TCP on :18462
|
Stable |
| iOS Physical Device | WebSocket → Mac relay (Bonjour auto-discovery) | Stable |
| iOS Physical Device (no relay) | Console logging via devicectl
|
Stable (one-way) |
| JVM/Desktop | In-memory (no transport) | Metrics collected, no export |
Why multiple artifacts? Kotlin's compiler plugin IR API changes between minor versions. Each artifact is compiled against the matching kotlin-compiler-embeddable to ensure binary compatibility.
See roadmap.md for the full iOS roadmap.
Make your AI coding tool understand Compose recomposition performance. The rebound-skill repo contains markdown-based instructions that any AI assistant can consume.
What it covers: Budget classes, violation diagnosis, skip rate analysis, stability optimization, CLI usage, IDE plugin workflow — all backed by the actual Rebound codebase.
Supports: Claude Code, Gemini CLI, Gemini (Android Studio), Cursor, Copilot, Codex, Windsurf, Amazon Q, and any markdown-consuming AI tool.
See aldefy/rebound-skill for setup instructions.
Full documentation at aldefy.github.io/compose-rebound.
# Build all modules
./gradlew build
# Build IDE plugin
./gradlew :rebound-ide:buildPlugin
# Output: rebound-ide/build/distributions/rebound-ide-0.1.0.zip
# Run sample app
./gradlew :sample:installDebugCopyright 2025 Adit Lal
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Compose recomposition budget monitor — catch runaway recompositions before they ship.
Rebound is a Kotlin compiler plugin that instruments every @Composable function with lightweight tracking calls. At runtime, it monitors recomposition rates against per-composable budgets, detects violations, and reports them via an Android Studio tool window, CLI, or logcat. Works on Android and iOS (Compose Multiplatform). Zero config required — just apply the Gradle plugin. The IDE plugin provides a 5-tab performance cockpit with live monitoring, hot spots ranking, timeline heatmap, stability analysis, and session history with VCS correlation.
@ReboundBudget annotation — override the inferred budget class for any composablesnapshot, summary, watch, ping commands over ADB socket1. Apply the Gradle plugin
// settings.gradle.kts
pluginManagement {
repositories {
gradlePluginPortal()
mavenCentral()
}
}// build.gradle.kts (app module)
plugins {
id("io.github.aldefy.rebound") version "0.2.1"
}2. Configure (optional)
rebound {
enabled.set(true) // default: true
debugOnly.set(true) // default: true — only instruments debug builds
}3. Run your app
Rebound auto-installs on first composition. Violations appear in logcat:
W/Rebound: [VIOLATION] MyScreen — 8 recomp/s (budget: 3, class: SCREEN)
Connect the IDE plugin or CLI for richer output.
The compiler plugin injects ReboundTracker.onCompositionEnter/Exit calls into every @Composable function at the IR level. The runtime tracks rates per sliding window and reports violations. The IDE plugin and CLI connect via LocalServerSocket("rebound") forwarded through ADB (Android) or via a WebSocket relay (iOS physical devices).
Platform support: The compiler plugin instruments all KMP targets. Android uses
LocalServerSocket+ ADB forward. iOS simulator uses a direct TCP server on:18462. iOS physical devices connect outbound to a Mac-side WebSocket relay via Bonjour auto-discovery, with console logging as fallback.
| Module | Description |
|---|---|
rebound-runtime |
KMP runtime library (Android, iOS, JVM). Full transport on all platforms. |
rebound-compiler |
Kotlin compiler plugin for Kotlin 2.0.x-2.1.x |
rebound-compiler-k2 |
Kotlin compiler plugin for Kotlin 2.2+ |
rebound-gradle |
Gradle plugin — auto-wires compiler + runtime, selects correct artifact |
rebound-ide |
Android Studio plugin — 5-tab performance cockpit with editor integration |
tools/rebound-relay |
Mac relay server — bridges iOS physical devices to CLI/IDE via WebSocket |
sample |
Sample Android app |
rebound {
enabled.set(true) // Master switch — set false to disable all instrumentation
debugOnly.set(true) // Only instrument debug builds (release builds get no overhead)
}Runtime toggles:
ReboundTracker.enabled = true // Master on/off at runtime
ReboundTracker.logCompositions = false // Per-composition logcat (throttled 1/s per composable)Each composable is auto-classified by IR heuristics:
| Budget Class | Rate/sec | Heuristic |
|---|---|---|
SCREEN |
3 | Name contains Screen or Page
|
CONTAINER |
10 | Has child @Composable calls |
INTERACTIVE |
30 | Default for unclassified |
LIST_ITEM |
60 | Inside LazyColumn/LazyRow/LazyGrid
|
ANIMATED |
120 | Calls animate*/Transition/Animation APIs |
LEAF |
5 | No child @Composable calls |
Screen or Page.derivedStateOf or move reads into a smaller scope.@Composable calls — Column, Row, Box, Scaffold content slots.@Composable calls in its body.animate*, Transition, Animation, or Animatable APIs.@Composable calls — Text(), Icon(), Image().@Composable calls in the function body.| Color | Condition | Status |
|---|---|---|
| Red | rate > budget | OVER |
| Yellow | rate > 70% of budget | NEAR |
| Green | rate <= 70% of budget | OK |
| Gray | not actively recomposing | — |
| Interaction State | Multiplier | Effect |
|---|---|---|
| IDLE | 1x | Normal budgets |
| SCROLLING | 2x | Budgets doubled during scroll |
| ANIMATING | 1.5x | Budgets increased during animation |
| USER_INPUT | 1.5x | Budgets increased during input |
// This composable uses tilt sensor — it's not a leaf, it's animated
@ReboundBudget(BudgetClass.ANIMATED)
@Composable
fun TiltDrivenSticker(offset: Offset) { ... }The Android Studio plugin (targets 2024.2.1.3+) provides a 5-tab performance cockpit, editor integration, and session persistence. Configure via Preferences > Tools > Rebound.
Monitor — Live composable tree with sparkline rate history per node. Scrolling event log at the bottom shows recomposition events, violations, and state changes in real time.
Hot Spots — Sortable flat table of all tracked composables, ranked by severity (OVER > NEAR > OK). Summary card at the top shows violation/warning/OK counts at a glance. Click any row to jump to source.
Timeline — Composable x time heatmap. Each cell is colored green/yellow/red based on budget status at that moment. Scroll back up to 60 minutes. Useful for correlating recomposition spikes with user interactions.
Stability — Parameter stability matrix showing SAME/DIFFERENT/STATIC/UNCERTAIN status per parameter for each composable. Cascade impact tree visualizes how unstable parameters propagate recompositions through the hierarchy.
History — Saved sessions stored in .rebound/sessions/. Each session is VCS-tagged with branch name and commit hash. Side-by-side comparison view for before/after regression analysis.
@Composable function declarations, reflecting live budget status.> 12/s | budget: 8/s | OVER | skip: 45%.Rebound: 45 composables | 3 violations.The plugin connects via adb forward tcp:18462 localabstract:rebound. Install from the rebound-ide build output.
The CLI auto-detects the connection path: direct TCP (iOS simulator or relay), ADB forward (Android), or devicectl console (iOS physical without relay).
./rebound-cli.sh snapshot # Full JSON metrics for all tracked composables
./rebound-cli.sh summary # Top 10 composables by recomposition rate
./rebound-cli.sh watch # Live updates every 1 second
./rebound-cli.sh ping # Health check → "pong"Or query directly:
echo "snapshot" | nc localhost 18462For iOS physical devices, the runtime connects outbound to a Mac-side relay via WebSocket. The device discovers the relay automatically via Bonjour.
# Build the relay (one-time)
./tools/build-relay.sh
# Start the relay on your Mac
./tools/rebound-relay
# → TCP :18462 (CLI/IDE), WebSocket :18463 (devices), Bonjour: _rebound._tcp
# Now run your app on the physical device (same WiFi network)
# The device auto-discovers the relay and connects
./rebound-cli.sh snapshot # works transparently through relayOverride Bonjour discovery with an env var if needed (e.g., different subnet):
REBOUND_RELAY_HOST=192.168.1.100:18463
| Kotlin Version | Compiler Artifact | Status |
|---|---|---|
| 2.0.x | rebound-compiler |
Stable |
| 2.1.x | rebound-compiler |
Stable |
| 2.2.x | rebound-compiler-kotlin-2.2 |
Stable |
| 2.3.x | rebound-compiler-kotlin-2.3 |
Stable |
The Gradle plugin auto-detects your project's Kotlin version and selects the correct compiler artifact. No manual configuration needed.
| Platform | Transport | Status |
|---|---|---|
| Android (device/emulator) |
LocalServerSocket + ADB forward |
Stable |
| iOS Simulator | Direct TCP on :18462
|
Stable |
| iOS Physical Device | WebSocket → Mac relay (Bonjour auto-discovery) | Stable |
| iOS Physical Device (no relay) | Console logging via devicectl
|
Stable (one-way) |
| JVM/Desktop | In-memory (no transport) | Metrics collected, no export |
Why multiple artifacts? Kotlin's compiler plugin IR API changes between minor versions. Each artifact is compiled against the matching kotlin-compiler-embeddable to ensure binary compatibility.
See roadmap.md for the full iOS roadmap.
Make your AI coding tool understand Compose recomposition performance. The rebound-skill repo contains markdown-based instructions that any AI assistant can consume.
What it covers: Budget classes, violation diagnosis, skip rate analysis, stability optimization, CLI usage, IDE plugin workflow — all backed by the actual Rebound codebase.
Supports: Claude Code, Gemini CLI, Gemini (Android Studio), Cursor, Copilot, Codex, Windsurf, Amazon Q, and any markdown-consuming AI tool.
See aldefy/rebound-skill for setup instructions.
Full documentation at aldefy.github.io/compose-rebound.
# Build all modules
./gradlew build
# Build IDE plugin
./gradlew :rebound-ide:buildPlugin
# Output: rebound-ide/build/distributions/rebound-ide-0.1.0.zip
# Run sample app
./gradlew :sample:installDebugCopyright 2025 Adit Lal
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.