
Aurora
From syxUI — written for both platforms, not translated between them.
Slow drifting colour blobs behind content. Four gradients and one blur.
Backgrounds · SwiftUI · Flutter · iOS 17 · Flutter 3.27
- background
- aurora
- gradient
- blur
- ambient
- animated
The actual source
AuroraBackground.swift
// Aurora · syxUI · https://syxui.dev/components/aurora-background
// Free in any project. Keep this line and credit syxUI where a person can read it.
import SwiftUI
/// A slow aurora: a few heavily blurred colour blobs drifting behind content.
///
/// Cheap by construction — four gradient ellipses and one blur, no Canvas, no
/// per-frame drawing. Drop it in a `ZStack` behind anything.
///
/// Leave `phase` nil and it drifts on its own. Pass a value in 0...1 to drive
/// the loop yourself, which is useful for scroll-linked backgrounds and makes
/// the motion deterministic.
struct AuroraBackground: View {
var colors: [Color] = [
Color(red: 0.45, green: 0.36, blue: 0.98),
Color(red: 0.96, green: 0.42, blue: 0.72),
Color(red: 0.29, green: 0.72, blue: 0.98),
Color(red: 0.99, green: 0.72, blue: 0.42),
]
var base: Color = Color(white: 0.99)
var blurRadius: CGFloat = 90
var period: Double = 14
var phase: Double? = nil
@Environment(\.accessibilityReduceMotion) private var reduceMotion
@State private var animatedDrift: Double = 0
/// 0 at rest, 1 fully drifted. An external phase is mapped through a
/// cosine so t = 0 and t = 1 land on the same frame and the loop closes.
private var drift: Double {
if let phase { return (1 - cos(phase * 2 * .pi)) / 2 }
return animatedDrift
}
var body: some View {
GeometryReader { geo in
let side = max(geo.size.width, geo.size.height)
ZStack {
base
blob(colors[0], size: side * 0.95, from: (-0.05, -0.12), to: (-0.22, -0.28), side: side)
blob(colors[1], size: side * 0.85, from: (0.10, -0.26), to: (0.26, -0.10), side: side)
blob(colors[2], size: side * 0.90, from: (0.04, 0.10), to: (-0.18, 0.24), side: side)
blob(colors[3], size: side * 0.70, from: (0.30, 0.14), to: (0.20, 0.26), side: side)
}
.blur(radius: blurRadius)
// Blur samples past the bounds, so clip after blurring or the
// edges wash out.
.clipped()
}
.ignoresSafeArea()
.onAppear(perform: startDrift)
.accessibilityHidden(true)
}
private func startDrift() {
guard phase == nil, !reduceMotion else { return }
withAnimation(.easeInOut(duration: period).repeatForever(autoreverses: true)) {
animatedDrift = 1
}
}
private func blob(
_ color: Color,
size: CGFloat,
from: (CGFloat, CGFloat),
to: (CGFloat, CGFloat),
side: CGFloat
) -> some View {
let x = from.0 + (to.0 - from.0) * drift
let y = from.1 + (to.1 - from.1) * drift
return Ellipse()
.fill(
RadialGradient(
colors: [color.opacity(0.85), color.opacity(0)],
center: .center,
startRadius: 0,
endRadius: size / 2
)
)
.frame(width: size, height: size * 0.8)
.offset(x: x * side, y: y * side)
}
}
#Preview {
AuroraBackground()
.frame(width: 400, height: 300)
}
iOS 17 · No dependencies
SwiftUI note. One 14-second animation drives all four blobs. There is no Canvas and no timer, so this is safe to leave running behind a full screen.
Dependencies
- SwiftUI
- No external dependencies
- Flutter
- No external dependencies
Requires iOS 17
Requires Flutter 3.27


