Skip to content

Color Orbs Background

Large, soft colored orbs drifting slowly over a dark base.

Color Orbs Background, playing
Download .swift
Origin
Original work
Found by
background · orbs · blur · blobs · glow · ambient · animated · fondo · orbes · difuminado · manchas · brillo · ambiente · animado

Large, soft colored orbs drifting slowly over a dark base.

With Reduce Motion on, the orbs hold still.

struct ColorOrbsBackgroundDemo: View {
var body: some View {
ZStack {
ColorOrbsBackground()
VStack(alignment: .leading, spacing: 8) {
Image(systemName: "moon.stars.fill")
.font(.title)
.accessibilityHidden(true)
Text("Evening Focus")
.font(.system(.title, design: .rounded, weight: .bold))
Text("Soft color moves behind your content without getting in its way.")
.font(.subheadline)
.foregroundStyle(.white.opacity(0.8))
}
.foregroundStyle(.white)
.padding(24)
.frame(maxWidth: .infinity, alignment: .leading)
.background(.ultraThinMaterial, in: .rect(cornerRadius: 28))
.environment(\.colorScheme, .dark)
.padding(24)
}
}
}
ParameterTypeDefaultDescription
colors[Color][.pink, .purple, .blue, .teal, .orange]One orb per color.
baseColorColor(red: 0.05, green: 0.04, blue: 0.13)
speedDouble0.4

Copy this code into your project, or download the file.

ColorOrbsBackground.swift
// SPDX-License-Identifier: MIT
// Copyright (c) 2026 Oscar Canton and SwiftUI Blocks contributors
import SwiftUI
/// Large, soft colored orbs drifting slowly over a dark base.
///
/// With Reduce Motion on, the orbs hold still.
struct ColorOrbsBackground: View {
/// One orb per color.
var colors: [Color] = [.pink, .purple, .blue, .teal, .orange]
var base: Color = Color(red: 0.05, green: 0.04, blue: 0.13)
var speed: Double = 0.4
@Environment(\.accessibilityReduceMotion) private var reduceMotion
var body: some View {
TimelineView(.animation(minimumInterval: 1.0 / 30.0, paused: reduceMotion)) { timeline in
Canvas(opaque: true) { context, size in
let time = reduceMotion ? 0 : timeline.date.timeIntervalSinceReferenceDate * speed
context.fill(Path(CGRect(origin: .zero, size: size)), with: .color(base))
context.blendMode = .screen
for (index, color) in colors.enumerated() {
drawOrb(index: index, color: color, at: time, in: context, size: size)
}
}
}
.ignoresSafeArea()
.accessibilityHidden(true)
}
/// A radial falloff to clear looks like a heavy blur and costs far less to redraw each frame.
private func drawOrb(index: Int, color: Color, at time: Double, in context: GraphicsContext, size: CGSize) {
let center = CGPoint(
x: (0.5 + 0.42 * sin(time * (0.5 + noise(index, 1) * 0.5) + noise(index, 2) * 2 * .pi)) * size.width,
y: (0.5 + 0.42 * cos(time * (0.4 + noise(index, 3) * 0.5) + noise(index, 4) * 2 * .pi)) * size.height
)
let radius = max(size.width, size.height) * (0.34 + noise(index, 5) * 0.14)
let falloff = Gradient(stops: [
.init(color: color.opacity(0.9), location: 0),
.init(color: color.opacity(0.45), location: 0.5),
.init(color: color.opacity(0), location: 1),
])
context.fill(
Path(ellipseIn: CGRect(x: center.x - radius, y: center.y - radius, width: radius * 2, height: radius * 2)),
with: .radialGradient(falloff, center: center, startRadius: 0, endRadius: radius)
)
}
/// A repeatable pseudo-random value in 0..<1 for one orb and one property.
private func noise(_ index: Int, _ salt: Double) -> Double {
let value = sin(Double(index) * 127.1 + salt * 311.7) * 43758.5453
return value - floor(value)
}
}
// MARK: - Demo
struct ColorOrbsBackgroundDemo: View {
var body: some View {
ZStack {
ColorOrbsBackground()
VStack(alignment: .leading, spacing: 8) {
Image(systemName: "moon.stars.fill")
.font(.title)
.accessibilityHidden(true)
Text("Evening Focus")
.font(.system(.title, design: .rounded, weight: .bold))
Text("Soft color moves behind your content without getting in its way.")
.font(.subheadline)
.foregroundStyle(.white.opacity(0.8))
}
.foregroundStyle(.white)
.padding(24)
.frame(maxWidth: .infinity, alignment: .leading)
.background(.ultraThinMaterial, in: .rect(cornerRadius: 28))
.environment(\.colorScheme, .dark)
.padding(24)
}
}
}
#Preview {
ColorOrbsBackgroundDemo()
}