Skip to content

Instantly share code, notes, and snippets.

@timsneath
Created August 27, 2026 04:07
Show Gist options
  • Select an option

  • Save timsneath/fdd03729072a56b6147e7a8e48742389 to your computer and use it in GitHub Desktop.

Select an option

Save timsneath/fdd03729072a56b6147e7a8e48742389 to your computer and use it in GitHub Desktop.
import Foundation
struct AnimatedBirthdayCake {
let name: String
// Tier character widths
private let topWidth = 21
private let midWidth = 25
private let baseWidth = 29
// ANSI Controls
private let clearScreen = "\u{001B}[2J\u{001B}[H"
private let hideCursor = "\u{001B}[?25l"
private let showCursor = "\u{001B}[?25h"
private let homeCursor = "\u{001B}[H"
// MARK: - Cake Renderer
func render(candleStates: [String], confetti: [String] = []) -> String {
var lines: [String] = []
// Optional top confetti line
if !confetti.isEmpty {
lines.append(confetti.joined(separator: " "))
} else {
lines.append("")
}
// 1. Candles (exact centering: 5 spaces indent)
let candlePad = String(repeating: " ", count: 5)
lines.append(candlePad + candleStates.joined(separator: " "))
// 2. Tier 1 - Top
let topPad = String(repeating: " ", count: (baseWidth - topWidth) / 2)
lines.append(topPad + "┌" + String(repeating: "─", count: topWidth - 2) + "┐")
lines.append(topPad + "│" + center("* H A P P Y *", width: topWidth - 2) + "│")
// 3. Tier 2 - Middle
let midPad = String(repeating: " ", count: (baseWidth - midWidth) / 2)
let step1 = (midWidth - topWidth) / 2
lines.append(midPad + "┌" + String(repeating: "─", count: step1 - 1) + "┴" + String(repeating: "─", count: topWidth - 2) + "┴" + String(repeating: "─", count: step1 - 1) + "┐")
lines.append(midPad + "│" + center("B I R T H D A Y", width: midWidth - 2) + "│")
// 4. Tier 3 - Base
let step2 = (baseWidth - midWidth) / 2
let nameTag = "\(name.uppercased())"
lines.append("┌" + String(repeating: "─", count: step2 - 1) + "┴" + String(repeating: "─", count: midWidth - 2) + "┴" + String(repeating: "─", count: step2 - 1) + "┐")
lines.append("│" + center(nameTag, width: baseWidth - 2) + "│")
lines.append("└" + String(repeating: "─", count: baseWidth - 2) + "┘")
return lines.joined(separator: "\n")
}
private func center(_ text: String, width: Int) -> String {
let leftPad = (width - text.count) / 2
let rightPad = width - text.count - leftPad
return String(repeating: " ", count: leftPad) + text + String(repeating: " ", count: rightPad)
}
// MARK: - Animation Sequence
func animate() async throws {
print(clearScreen + hideCursor, terminator: "")
defer { print(showCursor) }
// Phase 1: Flickering Candles (10 frames)
let flickerVariations = [
["🕯️", "🕯️", "🕯️", "🕯️", "🕯️"],
["✨", "🕯️", "🕯️", "✨", "🕯️"],
["🕯️", "✨", "🕯️", "🕯️", "✨"],
["🕯️", "🕯️", "✨", "🕯️", "🕯️"]
]
for i in 0..<12 {
let state = flickerVariations[i % flickerVariations.count]
print(homeCursor + render(candleStates: state) + "\n\n 🎂 Make a wish, Tim...")
try await Task.sleep(for: .milliseconds(250))
}
// Phase 2: Blowing out candles one by one (left to right)
var candles = ["🕯️", "🕯️", "🕯️", "🕯️", "🕯️"]
for i in 0..<5 {
candles[i] = "💨"
print(homeCursor + render(candleStates: candles) + "\n\n 💨 *Ffffffffff...* ")
try await Task.sleep(for: .milliseconds(220))
}
try await Task.sleep(for: .milliseconds(400))
// Phase 3: Confetti Burst Celebration (15 frames)
let confettiShapes = ["🎉", "✨", "🎈", "⭐️", "🎊", "🥳"]
for _ in 0..<16 {
let row = (0..<6).map { _ in confettiShapes.randomElement()! }
print(homeCursor + render(candleStates: ["💨", "💨", "💨", "💨", "💨"], confetti: row) + "\n\n 🎉 HAPPY BIRTHDAY, TIM! 🥳")
try await Task.sleep(for: .milliseconds(200))
}
}
}
// MARK: - Run
let cake = AnimatedBirthdayCake(name: "Tim")
try await cake.animate()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment