Skip to content

Instantly share code, notes, and snippets.

@m1ckc3s
Created June 27, 2025 23:04
Show Gist options
  • Save m1ckc3s/d221bcd875eeeb696b45d11551e60a7f to your computer and use it in GitHub Desktop.
Save m1ckc3s/d221bcd875eeeb696b45d11551e60a7f to your computer and use it in GitHub Desktop.
//
// DotPatternView.swift
// x.com/mickces
//
// Created by mick on 4/27/25.
//
import SwiftUI
public struct DotPatternView: View {
public var dotSize: CGFloat
public var spacing: CGFloat
public var color: Color
public var shimmerSpeed: Double
public var dxFactor: Double
public var dyFactor: Double
public var baseAlpha: Double
public var alphaMultiplier: Double
public init(
dotSize: CGFloat = 3,
spacing: CGFloat = 18,
color: Color = Color.gray.opacity(0.4),
shimmerSpeed: Double = 2.0,
dxFactor: Double = 0.25,
dyFactor: Double = 0.21,
baseAlpha: Double = 0.2,
alphaMultiplier: Double = 3.0
) {
self.dotSize = dotSize
self.spacing = spacing
self.color = color
self.shimmerSpeed = shimmerSpeed
self.dxFactor = dxFactor
self.dyFactor = dyFactor
self.baseAlpha = baseAlpha
self.alphaMultiplier = alphaMultiplier
}
public var body: some View {
TimelineView(.animation) { context in
shimmerCanvas(at: context.date.timeIntervalSinceReferenceDate)
}
}
private func shimmerCanvas(at time: TimeInterval) -> some View {
GeometryReader { geo in
let cols = Int(geo.size.width / spacing) + 2
let rows = Int(geo.size.height / spacing) + 2
Canvas { ctx, _ in
let dotRect = CGRect(origin: .zero,
size: CGSize(width: dotSize, height: dotSize))
for row in 0...rows {
for col in 0...cols {
let x = CGFloat(col) * spacing
let y = CGFloat(row) * spacing
let hash = UInt32((row &* 73856093) ^ (col &* 19349663))
let phase = Double(hash & 0xFF) / 255.0 * .pi * 2
let freq = 0.7 + Double((hash >> 8) & 0xFF) / 255.0
let dx = Double(col) * dxFactor
let dy = Double(row) * dyFactor
let baseWave = sin(time * 0.6 + dx) + cos(time * 0.4 + dy)
let pulse = sin(time * shimmerSpeed * freq + phase)
let blended = (pulse + baseWave) / 4.0
let alpha = baseAlpha + alphaMultiplier * abs(blended)
let dotColor = color.opacity(alpha)
ctx.fill(
Path(ellipseIn: dotRect.offsetBy(dx: x, dy: y)),
with: .color(dotColor)
)
}
}
}
}
.ignoresSafeArea()
.allowsHitTesting(false)
.accessibilityHidden(true)
// .blur(radius: 0.25) // optional blur the dots
.clipped()
}
}
// MARK: - Preview
#Preview {
DotPatternDemoView()
}
struct DotPatternDemoView: View {
@State private var colorOpacity: Double = 0.4
@State private var shimmerSpeed: Double = 2.0
@State private var dxFactor: Double = 0.25
@State private var dyFactor: Double = 0.21
@State private var baseAlpha: Double = 0.2
@State private var alphaMultiplier: Double = 3.0
var body: some View {
ZStack {
Color.black.ignoresSafeArea()
DotPatternView(
dotSize: 3,
spacing: 18,
color: Color.gray.opacity(colorOpacity),
shimmerSpeed: shimmerSpeed,
dxFactor: dxFactor,
dyFactor: dyFactor,
baseAlpha: baseAlpha,
alphaMultiplier: alphaMultiplier
)
.ignoresSafeArea()
VStack {
Spacer()
VStack(spacing: 12) {
HStack {
Text(String(format: "%.2f", colorOpacity))
.monospacedDigit()
Slider(value: $colorOpacity, in: 0.1...1)
Text(String(format: "%.2f", colorOpacity))
.monospacedDigit()
}
HStack {
Text(String(format: "%.2f", shimmerSpeed))
.monospacedDigit()
Slider(value: $shimmerSpeed, in: 0.5...5)
Text(String(format: "%.2f", shimmerSpeed))
.monospacedDigit()
}
HStack {
Text(String(format: "%.2f", dxFactor))
.monospacedDigit()
Slider(value: $dxFactor, in: 0.1...1, step: 0.05)
Text(String(format: "%.2f", dxFactor))
.monospacedDigit()
}
HStack {
Text(String(format: "%.2f", dyFactor))
.monospacedDigit()
Slider(value: $dyFactor, in: 0.1...1, step: 0.05)
Text(String(format: "%.2f", dyFactor))
.monospacedDigit()
}
HStack {
Text(String(format: "%.2f", baseAlpha))
.monospacedDigit()
Slider(value: $baseAlpha, in: 0.0...10.0, step: 0.1)
Text(String(format: "%.2f", baseAlpha))
.monospacedDigit()
}
HStack {
Text(String(format: "%.2f", alphaMultiplier))
.monospacedDigit()
Slider(value: $alphaMultiplier, in: 0.0...10.0, step: 0.1)
Text(String(format: "%.2f", alphaMultiplier))
.monospacedDigit()
}
}
.padding()
.background(
ZStack {
Color.black.opacity(0.6)
}
)
.cornerRadius(12)
.padding()
.foregroundColor(.white)
.tint(.white)
.controlSize(.small)
.font(.system(size: 14, design: .monospaced))
}
}
}
}

Comments are disabled for this gist.