Five Physics and Shader Effects That Push Jetpack Compose Past Simple Lists
Challenging the Limits of Interaction and Animation with Compose: A Full Analysis of the ComposeCraftLab Open-Source Lab
In modern mobile development, declarative UI (such as Jetpack Compose, Flutter, SwiftUI) has become the absolute mainstream. However, many developers' understanding of Compose is still stuck at the "writing simple lists and forms" stage.
To explore the rendering and physical interaction limits of Compose, I have officially open-sourced the project — ComposeCraftLab (Compose Visual and Interaction Lab).
- Project Open-Source Address: https://github.com/yangcyzhang/ComposeCraftLab.git
- Project Tech Stack: Compose Multiplatform 1.7.3 + Kotlin 2.1.0 (Supports Android / iOS)
This article will take you deep into the underlying source code of the project, dissecting the mathematical and rendering implementation schemes of five high-level special effects: Viscous Fluid Fusion, 3D Holographic Parallax, AGSL Plasma Quicksand, Bouncy Physical Elasticity, and Particle Gravity Galaxy!
🎨 Lab One: Viscous Fluid Fusion (Liquid Gooey Physics)
1. Visual Effects and Pain Points
In traditional Android, implementing a viscous fluid (where two circles stretch and merge like water droplets when close) usually relies on Gaussian blur + high-contrast color filter (Alpha Threshold Filter), which requires API 31+ and faces severe frame drops on some mid-to-low-end devices.
2. Mathematical Solution: Common Tangents and Quadratic Bezier Curves
In ComposeCraftLab, we adopted a 100% cross-platform, fully API-compatible, zero-latency pure mathematical solution:
- Step 1: Calculate the angle of the line connecting the centers of the two circles θ=atan2(dy,dx).
- Step 2: As the distance between the two circles increases, dynamically shrink the common tangent opening angle α (resistance deformation).
- Step 3: Use a quadratic Bezier curve
quadraticTowith the midpoint of the two circle centers as the control point to dynamically connect the tangent points of the two circles, forming a stretching bridge.
// Core tangent points and Bezier viscous connector construction
val path = Path().apply {
moveTo(ax1, ay1)
quadraticTo(cx1, cy1, bx2, by2) // Draw the upper stretching arc
lineTo(bx1, by1)
quadraticTo(cx2, cy2, ax2, ay2) // Draw the lower stretching reverse arc
close()
}
// Draw the viscous stretching body (gradient flowing light)
drawPath(path, brush = Brush.linearGradient(colors = listOf(Color(0xFF00ADB5), Color(0xFF9C27B0))))
3D Lab Two: 3D Interactive Flowing Light Card (3D Parallax Card)
1. Parallax Effect and Perspective Projection
This experiment achieves a glossy texture similar to a "holographic flash card." The card tilts in 3D space following finger drags, and the overlaid rainbow-colored polarizing light strip undergoes reverse physical displacement, simulating light refraction.
2. Core Formula and CameraDistance
In Compose, simply rotating the X/Y axes appears very "flat and folded." We must manually modify the camera distance parameter (cameraDistance) to enable a true 3D perspective projection effect:
Box(
modifier = Modifier
.graphicsLayer {
rotationX = rotateX.value // Controls up/down tilt
rotationY = rotateY.value // Controls left/right tilt
cameraDistance = 14f * density // Enables high-fidelity perspective projection
}
)
The flowing light reflection layer dynamically drives the Offset shift of the LinearGradient start and end points through the inverse increment of the rotation angle, allowing the flowing light beam to move naturally.
🔬 Lab Three: Jelly Physics Elastic Ball (Physics Spring Jelly)
1. Physics Modeling: Area-Conserving Stretch
When pressing and dragging the ball, it stretches like glue or jelly. To reflect the physical texture of "volume/area conservation," we make the sphere enlarge along the pulling axis while compressing correspondingly on the perpendicular axis:
stretchFactor = 1.0 + dragDistance / 250
squeezeFactor = 1.0 / stretchFactor
2. Resistance Rebound (Spring System)
Upon release, we use Compose's elastic Animatable to trigger a rebound. Setting the Stiffness to 150f and the DampingRatio to a low damping value of 0.35f, the sphere oscillates back and forth near the center point, vibrating and contracting at high frequency, full of elastic beauty.
// Rotate and scale drawing, combined with damped rebound
rotate(degrees = angleDeg, pivot = Offset(centerX, centerY)) {
scale(scaleX = stretchFactor, scaleY = squeezeFactor, pivot = Offset(centerX, centerY)) {
drawCircle(brush = gradient, radius = radius, center = Offset(centerX + dragLen, centerY))
}
}
Lab Four: AGSL Dazzling Shader — GPU-Level Art
Run GPU-level shader code to generate dynamic mathematical textures:
- Visual Effect: Dynamically generated quicksand and liquid metal textures, supporting a "gravity repulsion" effect triggered by finger touch.
- Technical Principle: Android 13+ introduced AGSL. We wrote an efficient Fragment Shader and drive it in real-time via RuntimeShader in Compose.
- Interaction Logic: Capture coordinates through PointerInput and pass them as uniforms to the GPU.
- Dynamic Rendering: Calculate dot products and cosine wave fields inside the shader to achieve real-time light and shadow flow.
🌌 Lab Five: Particle Gravity Galaxy (Particle Swarm System)
To demonstrate Compose's performance under high-frequency redrawing, this experiment renders 500 independent physical particles simultaneously on a Canvas:
- Gravity Formula: Each frame, every particle calculates the vector to the screen center (gravity center), adding centripetal acceleration to produce a swirling flow similar to a "spiral galaxy disk."
- Touch Repulsion: When a finger presses the screen, a repulsive force field is generated within a 300px x 300px range of the touch point. As particles orbit under gravity, they are smoothly pushed away by the force field wave generated by the finger, creating an extremely cool and shocking effect.
- Refresh Rate Synchronization: Driven by
withFrameMillisfor per-frame redrawing, perfectly aligning with the phone's physical refresh rate (60Hz/120Hz), 500 particles run stably at full frame rate.
💡 Development Insights and Best Practices
During the development of ComposeCraftLab, the following two hardcore pitfalls avoidance guides for high-performance Compose Canvas drawing were summarized:
- Avoid Allocating Objects in the Draw Phase: Objects like
Path,Brush,Paint,ColorFilter, etc., must be initialized and cached inremember. They must never be written inside theDrawScopeofCanvas, otherwise 60-120 redraws per second will cause frequent GC, leading to UI flickering and stuttering. - Utilize
graphicsLayerto Isolate Recomposition: For pure position/deformation changes like 3D rotation and scaling, they should be mounted onModifier.graphicsLayeras much as possible. This pushes the rendering directly to the GPU for matrix transformation, avoiding the overhead of triggering a recomposition of the entire UI tree.
🏁 Conclusion
Through ComposeCraftLab, we can see that Compose is by no means just a UI framework. It is a complete rendering system. Paired with Kotlin Multiplatform, we can use the same set of code to implement these cinematic-level visual interactions on iOS and Android. If you are also interested in UI rendering and animation design, welcome to the repository to Star it for exchange and learning: 👉 yangcyzhang/ComposeCraftLab