Skip to content

Instantly share code, notes, and snippets.

@linreal
Created January 25, 2026 07:52
Show Gist options
  • Select an option

  • Save linreal/65c8980777aa2751e24b319914dc9d39 to your computer and use it in GitHub Desktop.

Select an option

Save linreal/65c8980777aa2751e24b319914dc9d39 to your computer and use it in GitHub Desktop.
Jetpack Compose OrbitalLoader
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.LinearOutSlowInEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.StartOffset
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.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.drawscope.clipRect
import androidx.compose.ui.graphics.drawscope.translate
import androidx.compose.ui.graphics.drawscope.withTransform
import androidx.compose.ui.graphics.nativeCanvas
import androidx.compose.ui.unit.dp
import kotlin.math.PI
import kotlin.math.sin
import kotlin.random.Random
/**
* Cosmic color palette
*/
private object CosmicColors {
// Background
val deepSpace = Color(0xFF0F0F1A)
val spaceGradientEnd = Color(0xFF1A1A2E)
// Core sun
val coreCenter = Color(0xFFFFE4B5) // Warm white
val coreMiddle = Color(0xFFFFB347) // Orange
val coreEdge = Color(0xFFFF6B35) // Deep orange
val coreGlow = Color(0xFFFF8C42)
// Planets with complementary colors
val planet1 = Color(0xFF00D9FF) // Cyan
val planet1Glow = Color(0xFF00A8CC)
val planet2 = Color(0xFFBF5AF2) // Purple
val planet2Glow = Color(0xFF9D4EDD)
val planet3 = Color(0xFF32D74B) // Green
val planet3Glow = Color(0xFF28A745)
// Accent
val starColor = Color(0xFFFFFFFF)
val orbitLine = Color(0xFF2A2A4A)
// Energy rings
val energyRing = Color(0xFFFF6B35)
}
/**
* Star data class for background
*/
private data class Star(
val x: Float,
val y: Float,
val radius: Float,
val alpha: Float,
val twinkleSpeed: Float
)
/**
* Enhanced Orbital Loader with cosmic aesthetics
*/
@Composable
fun OrbitalLoader(
modifier: Modifier = Modifier
) {
// Generate stars once and remember them
val stars = remember {
List(50) {
Star(
x = Random.nextFloat(),
y = Random.nextFloat(),
radius = Random.nextFloat() * 1.5f + 0.5f,
alpha = Random.nextFloat() * 0.5f + 0.3f,
twinkleSpeed = Random.nextFloat() * 2000f + 1000f
)
}
}
val infiniteTransition = rememberInfiniteTransition(label = "cosmic")
// ORBITAL ROTATIONS
val orbit1Angle by infiniteTransition.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(
animation = tween(4000, easing = LinearEasing)
),
label = "orbit1"
)
val orbit2Angle by infiniteTransition.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(
animation = tween(6000, easing = LinearEasing)
),
label = "orbit2"
)
val orbit3Angle by infiniteTransition.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(
animation = tween(9000, easing = LinearEasing)
),
label = "orbit3"
)
val moonAngle by infiniteTransition.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(
animation = tween(1500, easing = LinearEasing)
),
label = "moon"
)
// EFFECTS ANIMATIONS
val corePulse by infiniteTransition.animateFloat(
initialValue = 0.9f,
targetValue = 1.1f,
animationSpec = infiniteRepeatable(
animation = tween(2000, easing = FastOutSlowInEasing),
repeatMode = RepeatMode.Reverse
),
label = "pulse"
)
val starTwinkle by infiniteTransition.animateFloat(
initialValue = 0f,
targetValue = 1f,
animationSpec = infiniteRepeatable(
animation = tween(3000, easing = LinearEasing)
),
label = "twinkle"
)
// Energy ring expansion (0 to 1, then resets)
val ringExpansion by infiniteTransition.animateFloat(
initialValue = 0f,
targetValue = 1f,
animationSpec = infiniteRepeatable(
animation = tween(3000, easing = LinearOutSlowInEasing)
),
label = "ring"
)
val ringExpansion2 by infiniteTransition.animateFloat(
initialValue = 0f,
targetValue = 1f,
animationSpec = infiniteRepeatable(
animation = tween(3000, easing = LinearOutSlowInEasing),
initialStartOffset = StartOffset(1500) // Offset for staggered effect
),
label = "ring2"
)
Box(
modifier = modifier
.size(300.dp)
.background(
Brush.radialGradient(
colors = listOf(
CosmicColors.spaceGradientEnd,
CosmicColors.deepSpace
)
)
)
) {
Canvas(modifier = Modifier.matchParentSize()) {
val center = Offset(size.width / 2, size.height / 2)
// Orbital radii
val orbit1Radius = size.minDimension * 0.15f
val orbit2Radius = size.minDimension * 0.27f
val orbit3Radius = size.minDimension * 0.40f
val moonOrbitRadius = size.minDimension * 0.06f
// Body sizes
val coreRadius = size.minDimension * 0.07f * corePulse
val planetRadius = size.minDimension * 0.025f
val moonRadius = size.minDimension * 0.012f
// Number of trail segments
val trailCount = 12
// LAYER 0: STARFIELD BACKGROUND
drawStarfield(stars, starTwinkle)
// LAYER 1: ORBIT PATHS (subtle guide lines)
listOf(orbit1Radius, orbit2Radius, orbit3Radius).forEach { radius ->
drawCircle(
color = CosmicColors.orbitLine,
radius = radius,
center = center,
style = Stroke(width = 1f)
)
}
// LAYER 2: EXPANDING ENERGY RINGS FROM CORE
drawEnergyRing(center, coreRadius, orbit1Radius * 1.5f, ringExpansion)
drawEnergyRing(center, coreRadius, orbit1Radius * 1.5f, ringExpansion2)
// LAYER 3: PLANET TRAILS (drawn before planets for correct z-order)
// Using the SAME transformation logic, just at previous angles
// Trail for Planet 1
drawOrbitalTrail(
center = center,
orbitRadius = orbit1Radius,
currentAngle = orbit1Angle,
trailCount = trailCount,
planetRadius = planetRadius,
color = CosmicColors.planet1
)
// Trail for Planet 2
drawOrbitalTrail(
center = center,
orbitRadius = orbit2Radius,
currentAngle = orbit2Angle,
trailCount = trailCount,
planetRadius = planetRadius * 1.1f,
color = CosmicColors.planet2
)
// Trail for Planet 3
drawOrbitalTrail(
center = center,
orbitRadius = orbit3Radius,
currentAngle = orbit3Angle,
trailCount = trailCount + 4, // Longer trail for outer planet
planetRadius = planetRadius * 1.2f,
color = CosmicColors.planet3
)
// LAYER 4: THE CORE (Central Star)
drawGlowingCore(center, coreRadius)
// LAYER 5: PLANETS WITH GLOW
// Same transformation principles as before!
// Planet 1 - Inner orbit
withTransform({
rotate(degrees = orbit1Angle, pivot = center)
translate(left = center.x + orbit1Radius, top = center.y)
}) {
drawGlowingPlanet(
color = CosmicColors.planet1,
glowColor = CosmicColors.planet1Glow,
radius = planetRadius
)
}
// Planet 2 - Middle orbit (counter-rotating) with moon
withTransform({
rotate(degrees = orbit2Angle, pivot = center)
translate(left = center.x + orbit2Radius, top = center.y)
}) {
drawGlowingPlanet(
color = CosmicColors.planet2,
glowColor = CosmicColors.planet2Glow,
radius = planetRadius * 1.1f
)
// Moon orbiting this planet
withTransform({
rotate(degrees = moonAngle, pivot = Offset.Zero)
translate(left = moonOrbitRadius, top = 0f)
}) {
// Moon glow
drawCircle(
brush = Brush.radialGradient(
colors = listOf(
CosmicColors.planet2.copy(alpha = 0.4f),
Color.Transparent
),
center = Offset.Zero,
radius = moonRadius * 3f
),
radius = moonRadius * 3f,
center = Offset.Zero
)
drawCircle(
color = CosmicColors.planet2.copy(alpha = 0.8f),
radius = moonRadius,
center = Offset.Zero
)
}
}
// Planet 3 - Outer orbit
withTransform({
rotate(degrees = orbit3Angle, pivot = center)
translate(left = center.x + orbit3Radius, top = center.y)
}) {
drawGlowingPlanet(
color = CosmicColors.planet3,
glowColor = CosmicColors.planet3Glow,
radius = planetRadius * 1.2f
)
// Two moons for the outer planet
withTransform({
rotate(degrees = -moonAngle, pivot = Offset.Zero)
translate(left = moonOrbitRadius * 1.2f, top = 0f)
}) {
drawCircle(
color = CosmicColors.planet3.copy(alpha = 0.7f),
radius = moonRadius,
center = Offset.Zero
)
}
withTransform({
rotate(degrees = moonAngle + 180f, pivot = Offset.Zero)
translate(left = moonOrbitRadius * 0.8f, top = 0f)
}) {
drawCircle(
color = CosmicColors.planet3.copy(alpha = 0.5f),
radius = moonRadius * 0.7f,
center = Offset.Zero
)
}
}
}
}
}
/**
* Draws twinkling stars in the background
*/
private fun DrawScope.drawStarfield(stars: List<Star>, twinklePhase: Float) {
stars.forEach { star ->
// Calculate twinkle: each star has its own phase based on twinkleSpeed
val twinkle = (sin((twinklePhase * 2 * PI + star.twinkleSpeed).toFloat()) + 1f) / 2f
val currentAlpha = star.alpha * (0.5f + 0.5f * twinkle)
drawCircle(
color = CosmicColors.starColor.copy(alpha = currentAlpha),
radius = star.radius,
center = Offset(star.x * size.width, star.y * size.height)
)
}
}
/**
* Draws an expanding energy ring from the core
*/
private fun DrawScope.drawEnergyRing(
center: Offset,
startRadius: Float,
endRadius: Float,
expansion: Float
) {
val currentRadius = startRadius + (endRadius - startRadius) * expansion
val alpha = (1f - expansion) * 0.6f // Fade out as it expands
drawCircle(
color = CosmicColors.energyRing.copy(alpha = alpha),
radius = currentRadius,
center = center,
style = Stroke(width = 2f * (1f - expansion * 0.5f)) // Thinner as it expands
)
}
/**
* Draws the central glowing core/star
*/
private fun DrawScope.drawGlowingCore(center: Offset, radius: Float) {
// Outer glow (largest, most transparent)
drawCircle(
brush = Brush.radialGradient(
colors = listOf(
CosmicColors.coreGlow.copy(alpha = 0.3f),
CosmicColors.coreGlow.copy(alpha = 0.1f),
Color.Transparent
),
center = center,
radius = radius * 4f
),
radius = radius * 4f,
center = center
)
// Middle glow
drawCircle(
brush = Brush.radialGradient(
colors = listOf(
CosmicColors.coreGlow.copy(alpha = 0.5f),
CosmicColors.coreGlow.copy(alpha = 0.2f),
Color.Transparent
),
center = center,
radius = radius * 2.5f
),
radius = radius * 2.5f,
center = center
)
// Core body with gradient
drawCircle(
brush = Brush.radialGradient(
colors = listOf(
CosmicColors.coreCenter,
CosmicColors.coreMiddle,
CosmicColors.coreEdge
),
center = center,
radius = radius
),
radius = radius,
center = center
)
}
/**
* Draws an orbital trail behind a planet
*
* The trail is just multiple circles drawn at PREVIOUS positions.
* We use the same rotate+translate pattern, just with earlier angles.
* This demonstrates that transformation logic can be reused!
*/
private fun DrawScope.drawOrbitalTrail(
center: Offset,
orbitRadius: Float,
currentAngle: Float,
trailCount: Int,
planetRadius: Float,
color: Color
) {
for (i in 1..trailCount) {
// Each trail segment is at a previous angle
val trailAngle = currentAngle - (i * 3f) // 3 degrees apart
val alpha = (1f - (i.toFloat() / trailCount)) * 0.4f
val trailRadius = planetRadius * (1f - (i.toFloat() / trailCount) * 0.5f)
withTransform({
rotate(degrees = trailAngle, pivot = center)
translate(left = center.x + orbitRadius, top = center.y)
}) {
drawCircle(
color = color.copy(alpha = alpha),
radius = trailRadius,
center = Offset.Zero
)
}
}
}
/**
* Draws a planet with glow effect at the current (transformed) origin
*/
private fun DrawScope.drawGlowingPlanet(
color: Color,
glowColor: Color,
radius: Float
) {
// Outer glow
drawCircle(
brush = Brush.radialGradient(
colors = listOf(
glowColor.copy(alpha = 0.4f),
glowColor.copy(alpha = 0.1f),
Color.Transparent
),
center = Offset.Zero,
radius = radius * 4f
),
radius = radius * 4f,
center = Offset.Zero
)
// Inner glow
drawCircle(
brush = Brush.radialGradient(
colors = listOf(
color.copy(alpha = 0.6f),
Color.Transparent
),
center = Offset.Zero,
radius = radius * 2f
),
radius = radius * 2f,
center = Offset.Zero
)
// Planet body
drawCircle(
brush = Brush.radialGradient(
colors = listOf(
color,
glowColor
),
center = Offset(-radius * 0.3f, -radius * 0.3f), // Off-center for 3D effect
radius = radius * 1.5f
),
radius = radius,
center = Offset.Zero
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment