跪拜 Guibai
← Back to the summary

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).

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.

gooey_liquid_demo.gif

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:

// 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.

parallax_card_demo.gif

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

physics_spring_demo.gif

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:

  1. Visual Effect: Dynamically generated quicksand and liquid metal textures, supporting a "gravity repulsion" effect triggered by finger touch.
  2. Technical Principle: Android 13+ introduced AGSL. We wrote an efficient Fragment Shader and drive it in real-time via RuntimeShader in Compose.
  3. Interaction Logic: Capture coordinates through PointerInput and pass them as uniforms to the GPU.
  4. Dynamic Rendering: Calculate dot products and cosine wave fields inside the shader to achieve real-time light and shadow flow.

shader_sandbox_demo.gif

🌌 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:

  1. 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."
  2. 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.
  3. Refresh Rate Synchronization: Driven by withFrameMillis for per-frame redrawing, perfectly aligning with the phone's physical refresh rate (60Hz/120Hz), 500 particles run stably at full frame rate.

particle_system_demo.gif


💡 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:

  1. Avoid Allocating Objects in the Draw Phase: Objects like Path, Brush, Paint, ColorFilter, etc., must be initialized and cached in remember. They must never be written inside the DrawScope of Canvas, otherwise 60-120 redraws per second will cause frequent GC, leading to UI flickering and stuttering.
  2. Utilize graphicsLayer to Isolate Recomposition: For pure position/deformation changes like 3D rotation and scaling, they should be mounted on Modifier.graphicsLayer as 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