Skip to content

Instantly share code, notes, and snippets.

@Kyriakos-Georgiopoulos
Created June 15, 2025 17:05
Show Gist options
  • Save Kyriakos-Georgiopoulos/9430d837f9d2fdc4300c4bee2a7ef7c2 to your computer and use it in GitHub Desktop.
Save Kyriakos-Georgiopoulos/9430d837f9d2fdc4300c4bee2a7ef7c2 to your computer and use it in GitHub Desktop.
RealisticCd
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.*
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.drawscope.rotate
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.PI
@Composable
fun RealisticCd(
modifier: Modifier = Modifier
) {
val infiniteTransition = rememberInfiniteTransition("CD Sway")
val rotation by infiniteTransition.animateFloat(
initialValue = -10f,
targetValue = 10f,
animationSpec = infiniteRepeatable(
animation = tween(3000, easing = LinearEasing),
repeatMode = RepeatMode.Reverse
),
label = "Sway Angle"
)
Box(
modifier = modifier
.size(360.dp)
.graphicsLayer { rotationZ = rotation }
.drawBehind {
val radius = size.minDimension / 2f
val center = Offset(size.width / 2f, size.height / 2f)
drawCircle(
brush = Brush.radialGradient(
colors = listOf(
Color(0xFFD7D7D7),
Color(0xFFAFAFAF),
Color(0xFF7F7F7F)
),
center = center,
radius = radius
),
center = center,
radius = radius
)
// Outer rim with a glossy highlight
drawCircle(
brush = Brush.verticalGradient(
colors = listOf(
Color.White.copy(alpha = 0.4f),
Color.Transparent,
Color.White.copy(alpha = 0.1f)
),
startY = center.y - radius,
endY = center.y + radius
),
radius = radius,
center = center,
style = Stroke(width = radius * 0.02f)
)
// Rainbow rays
drawRainbowBeam(start = 50f, sweep = 36f, center, radius)
drawRainbowBeam(start = 230f, sweep = 36f, center, radius)
// Light-colored rays
drawBeam(start = 140f, sweep = 34f, center, radius)
drawBeam(start = 320f, sweep = 34f, center, radius)
// Inner circles to define CD hole
drawCircle(color = Color(0xFFCCCCCC), center = center, radius = radius * 0.25f)
drawCircle(color = Color.Black, center = center, radius = radius * 0.1f)
// Animated sheen highlight
val sheen = Offset(
(center.x + cos(Math.toRadians(rotation.toDouble())) * radius * 0.25f).toFloat(),
(center.y + sin(Math.toRadians(rotation.toDouble())) * radius * 0.25f).toFloat()
)
drawCircle(
brush = Brush.radialGradient(
colors = listOf(Color.White.copy(alpha = 0.15f), Color.Transparent),
center = sheen,
radius = radius * 0.8f
),
center = center,
radius = radius
)
// Inner rim highlight
drawCircle(
brush = Brush.verticalGradient(
colors = listOf(
Color.White.copy(alpha = 0.5f),
Color.Transparent,
Color.White.copy(alpha = 0.08f)
),
startY = center.y - radius / 3.8f,
endY = center.y + radius / 3.8f
),
radius = radius * 0.23f,
center = center,
style = Stroke(width = radius * 0.015f)
)
}
)
}
private fun DrawScope.drawRainbowBeam(start: Float, sweep: Float, center: Offset, radius: Float) {
val lines = 200
val angleStep = sweep / lines
for (i in 0 until lines) {
val ang = start + i * angleStep
val rad = Math.toRadians(ang.toDouble()).toFloat()
val t = i / (lines - 1f)
val hue = 320 * t + 20
val color = Color.hsv(hue, 0.9f, 0.9f).copy(alpha = 0.4f * sin(PI * t).toFloat())
val inner = Offset(
center.x + cos(rad) * radius * 0.2f,
center.y + sin(rad) * radius * 0.2f
)
val outer = Offset(
center.x + cos(rad) * radius,
center.y + sin(rad) * radius
)
drawLine(
color = color,
start = inner,
end = outer,
strokeWidth = 0.3f,
cap = StrokeCap.Butt,
blendMode = BlendMode.Plus
)
}
}
private fun DrawScope.drawBeam(start: Float, sweep: Float, center: Offset, radius: Float) {
val lines = 200
val angleStep = sweep / lines
for (i in 0 until lines) {
val ang = start + i * angleStep
val rad = Math.toRadians(ang.toDouble()).toFloat()
val t = i / (lines - 1f)
val r = lerp(1.0f, 0.9f, t)
val g = 0.95f // nearly constant
val b = lerp(0.8f, 1.0f, t)
val color = Color(r, g, b, 0.4f * sin(PI * t).toFloat())
val inner = Offset(
center.x + cos(rad) * radius * 0.2f,
center.y + sin(rad) * radius * 0.2f
)
val outer = Offset(
center.x + cos(rad) * radius,
center.y + sin(rad) * radius
)
drawLine(
color = color,
start = inner,
end = outer,
strokeWidth = 0.3f,
cap = StrokeCap.Butt,
blendMode = BlendMode.Plus
)
}
}
private fun lerp(start: Float, end: Float, t: Float): Float = start + t * (end - start)
@Preview
@Composable
fun RealisticCdPreview() {
Box(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFF101010)),
contentAlignment = Alignment.Center
) {
RealisticCd()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment