Skip to content
Beams preview
Painted live in the browser. The committed still and loop are rendered from the SwiftUI source.

Beams

From syxUI — written for both platforms, not translated between them.

Angled light shafts drifting across a dark field, drawn in one Canvas pass.

Backgrounds · SwiftUI · Flutter · iOS 17 · Flutter 3.27

  • background
  • beams
  • light
  • dark
  • ambient
  • animated

The actual source

Beams.swift
// Beams · syxUI · https://syxui.dev/components/beams
// Free in any project. Keep this line and credit syxUI where a person can read it.

import SwiftUI

/// Angled light shafts drifting across a dark field.
///
/// One `Canvas` pass draws every beam, so adding beams costs a rectangle fill
/// rather than a view. Leave `phase` nil to drift on its own; pass 0...1 to
/// drive the loop yourself.
struct Beams: View {
    var count: Int = 7
    var angle: Angle = .degrees(-24)
    var tint: Color = Color(red: 0.76, green: 0.84, blue: 1.0)
    var background: Color = Color(red: 0.05, green: 0.05, blue: 0.09)
    var period: Double = 12
    var phase: Double? = nil

    @Environment(\.accessibilityReduceMotion) private var reduceMotion
    @State private var animated: Double = 0

    private var current: Double { phase ?? animated }

    var body: some View {
        Canvas { context, size in
            context.fill(Path(CGRect(origin: .zero, size: size)), with: .color(background))

            let span = size.width + size.height
            context.rotate(by: angle)
            context.addFilter(.blur(radius: 16))

            for index in 0..<count {
                // Each beam has its own width and speed, so they never line up
                // into a visible repeating band.
                let seed = Double(index)
                let width = span * (0.018 + 0.030 * fract(seed * 0.37))
                let speed = 0.6 + fract(seed * 0.73)
                let offset = fract(current * speed + seed / Double(count))
                let x = -span * 0.4 + offset * span * 1.6
                let opacity = 0.22 + 0.40 * fract(seed * 0.51)

                let rect = CGRect(x: x, y: -span * 0.5, width: width, height: span * 1.6)
                context.fill(
                    Path(roundedRect: rect, cornerRadius: width / 2),
                    with: .linearGradient(
                        Gradient(stops: [
                            .init(color: tint.opacity(0), location: 0),
                            .init(color: tint.opacity(opacity), location: 0.42),
                            .init(color: tint.opacity(0), location: 1),
                        ]),
                        startPoint: CGPoint(x: rect.midX, y: rect.minY),
                        endPoint: CGPoint(x: rect.midX, y: rect.maxY)
                    )
                )
            }
        }
        .onAppear(perform: start)
        .accessibilityHidden(true)
    }

    /// Fractional part, kept positive. Used as a cheap deterministic hash.
    private func fract(_ value: Double) -> Double {
        let result = value - value.rounded(.down)
        return result < 0 ? result + 1 : result
    }

    private func start() {
        guard phase == nil, !reduceMotion else { return }
        withAnimation(.linear(duration: period).repeatForever(autoreverses: false)) {
            animated = 1
        }
    }
}

#Preview {
    Beams()
        .frame(width: 420, height: 300)
}
iOS 17 · No dependencies

SwiftUI note. Every beam is one rounded rect inside a single Canvas, blurred by a context filter rather than a per-beam .blur modifier.

Dependencies

SwiftUI
No external dependencies

Requires iOS 17

Flutter
No external dependencies

Requires Flutter 3.27