What You Will Build

A button with a pulsating neon glow effect behind it, created using an infinitely animating blurred background layer. The glow alpha and blur radius breathe in and out, making the button look like it is emitting light. This technique is perfect for CTAs in dark-themed apps, gaming UIs, or music players.

Why This Pattern Matters

Glow effects draw the eye to the most important action on screen. Unlike simple elevation shadows, a colored glow reinforces your brand palette and creates depth on dark backgrounds where Material elevation alone falls flat.

Key Compose Concepts

  • rememberInfiniteTransition for perpetual animation without recomposition overhead
  • Modifier.blur() to create the glow layer
  • Brush.horizontalGradient for multi-color effects

The Glow Button Composable

@Composable
fun GlowingButton(text: String, colors: List<Color>) {
    val infiniteTransition = rememberInfiniteTransition(label = "glow")
    val glowAlpha by infiniteTransition.animateFloat(
        initialValue = 0.4f,
        targetValue = 0.8f,
        animationSpec = infiniteRepeatable(
            animation = tween(1500, easing = FastOutSlowInEasing),
            repeatMode = RepeatMode.Reverse
        ),
        label = "glowAlpha"
    )
    val glowSize by infiniteTransition.animateFloat(
        initialValue = 10f,
        targetValue = 25f,
        animationSpec = infiniteRepeatable(
            animation = tween(1500, easing = FastOutSlowInEasing),
            repeatMode = RepeatMode.Reverse
        ),
        label = "glowSize"
    )

    Box(contentAlignment = Alignment.Center) {
        // Glow layer behind the button
        Box(
            modifier = Modifier
                .width(220.dp)
                .height(55.dp)
                .blur(glowSize.dp)
                .background(
                    Brush.horizontalGradient(
                        colors.map { it.copy(alpha = glowAlpha) }
                    ),
                    RoundedCornerShape(28.dp)
                )
        )

        // Actual button
        Box(
            modifier = Modifier
                .width(200.dp)
                .height(50.dp)
                .clip(RoundedCornerShape(25.dp))
                .background(
                    Brush.horizontalGradient(colors),
                    RoundedCornerShape(25.dp)
                )
                .clickable {},
            contentAlignment = Alignment.Center
        ) {
            Text(
                text,
                color = Color.White,
                fontWeight = FontWeight.Bold,
                fontSize = 16.sp
            )
        }
    }
}

Using Multiple Glow Variants

@Composable
fun GlowButtonScreen() {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        GlowingButton("Neon Blue", listOf(Color(0xFF00D4FF), Color(0xFF0099FF)))
        Spacer(modifier = Modifier.height(30.dp))
        GlowingButton("Purple Glow", listOf(Color(0xFFBB86FC), Color(0xFF6200EA)))
        Spacer(modifier = Modifier.height(30.dp))
        GlowingButton("Fire Glow", listOf(Color(0xFFFF6B35), Color(0xFFFF0000)))
    }
}

Tips and Pitfalls

  • Use a dark background. The glow effect is invisible on white or light surfaces because the blurred color cannot contrast enough.
  • Modifier.blur() requires API 31+. On older devices, fall back to a simple shadow with Modifier.shadow().
  • Align tween durations for alpha and blur so they breathe in sync. Mismatched durations cause jittery pulsing.
  • Performance: Blur is GPU-intensive. Limit to one or two glowing buttons per screen.

Get New Tutorials by Email

No spam. Just clear, practical breakdowns you can apply right away.

Enjoy this tutorial?

Get new practical tech tutorials in your inbox.