
Neon Text
From syxUI — written for both platforms, not translated between them.
A neon tube with a slow hum and the occasional stutter, like real glass.
Text Effects · SwiftUI · Flutter · iOS 17 · Flutter 3.27
- text
- neon
- glow
- flicker
- sign
- dark
- animation
The actual source
NeonText.swift
// Neon Text · syxUI · https://syxui.dev/components/neon-text
// Free in any project. Keep this line and credit syxUI where a person can read it.
import SwiftUI
/// A neon tube with a slow hum and the occasional stutter, like real glass.
///
/// Four stacked shadow falloffs read as glass and gas rather than a drop
/// shadow. Brightness is driven by a slow sine hum plus a hashed, stepped
/// flicker so it never repeats identically — a `TimelineView` samples both
/// every frame since neither is animatable through `withAnimation`.
struct NeonText: View {
var text: String = "OPEN"
var tube: Color = Color(red: 1.000, green: 0.176, blue: 0.584)
var flicker: Double = 0.45
/// Multiplies all four shadow radii.
var glow: Double = 1.0
var hum: Bool = true
var background: Color = Color(red: 0.043, green: 0.043, blue: 0.071)
/// Preview override. When set, the component renders that point of one loop
/// instead of driving its own clock.
var phase: Double? = nil
@Environment(\.accessibilityReduceMotion) private var reduceMotion
private let humPeriod: Double = 3.1
private let bucketMs: Double = 80
private let averageDipEveryMs: Double = 2600
var body: some View {
ZStack {
background
if reduceMotion && phase == nil {
tube(brightness: 1)
} else {
TimelineView(.animation) { context in
let seconds = phase.map { $0 * humPeriod }
?? context.date.timeIntervalSinceReferenceDate
tube(brightness: brightness(at: seconds))
}
}
}
.accessibilityElement(children: .ignore)
.accessibilityLabel(text)
}
@ViewBuilder
private func tube(brightness b: Double) -> some View {
Text(text)
.font(.system(size: 44, weight: .bold, design: .rounded))
.foregroundStyle(.white.opacity(0.94 * b))
.shadow(color: tube.opacity(0.90 * b), radius: 2 * glow)
.shadow(color: tube.opacity(0.70 * b), radius: 6 * glow)
.shadow(color: tube.opacity(0.45 * b), radius: 16 * glow)
.shadow(color: tube.opacity(0.25 * b), radius: 38 * glow)
.overlay {
Text(text)
.font(.system(size: 44, weight: .bold, design: .rounded))
.foregroundStyle(tube)
.blur(radius: 12 * glow)
.blendMode(.plusLighter)
.opacity(b)
}
.compositingGroup()
}
/// Slow sine hum plus a hashed, stepped flicker dip. The flicker bucket is
/// a pure function of time so a preview can sample any frame and still
/// land inside a representative dip.
private func brightness(at seconds: Double) -> Double {
let humValue = hum ? sin(seconds / humPeriod * 2 * .pi) * 0.06 : 0
var value = 1.0 + humValue
guard flicker > 0 else { return value }
let bucket = floor(seconds * 1000 / bucketMs)
let dipsPerBucket = bucketMs / averageDipEveryMs
let roll = hash(bucket)
if roll < dipsPerBucket * flicker * 4 {
// Two dip depths, chosen by a second, independent hash so the
// stutter reads as glass arcing rather than a metronome.
let depth = hash(bucket + 1000) < 0.5 ? 0.35 : 0.62
value = depth
}
return value
}
/// Deterministic pseudo-random value in `0..<1` from an integer bucket —
/// no `Int` seed needed, so previews reproduce exactly.
private func hash(_ n: Double) -> Double {
let x = sin(n * 12.9898) * 43758.5453
return x - floor(x)
}
}
#Preview {
NeonText()
.frame(width: 320, height: 160)
}
iOS 17 · No dependencies
SwiftUI note. Four chained `.shadow` modifiers plus one `.plusLighter` overlay is the whole cost — no offscreen pass beyond what `.compositingGroup()` already does. Push `glow` down before touching anything else if it needs to run on an older device.
Dependencies
- SwiftUI
- No external dependencies
- Flutter
- No external dependencies
Requires iOS 17
Requires Flutter 3.27


