Skip to content

Instantly share code, notes, and snippets.

@adam-zethraeus
Last active July 15, 2025 08:54
Show Gist options
  • Select an option

  • Save adam-zethraeus/27b97f2726a8aae1978ba2505300a5ac to your computer and use it in GitHub Desktop.

Select an option

Save adam-zethraeus/27b97f2726a8aae1978ba2505300a5ac to your computer and use it in GitHub Desktop.
VisualHash.swift (Value -> Emoji / Pastel / Neon)
import CryptoKit
import SwiftUI
#if canImport(UIKit)
import UIKit
#elseif canImport(AppKit)
import AppKit
#endif
public enum VisualHash {
public static func pastel(from item: some Codable) -> Color {
let raw = try! JSONEncoder().encode(item)
let hash = SHA256.hash(data: raw)
let data = Array(hash)
let r = Double(data[0]) / 255.0
let g = Double(data[1]) / 255.0
let b = Double(data[2]) / 255.0
let reader = Color(
red: r,
green: g,
blue: b
)
let pastelHue = reader.hsba.hue
let pastelSaturation = 0.05 + (reader.hsba.saturation * 0.35)
let pastelBrightness = 0.6 + (reader.hsba.brightness * 0.4)
return Color(
hue: pastelHue,
saturation: pastelSaturation,
brightness: pastelBrightness
)
}
public static func neon(from item: some Codable) -> Color {
let raw = try! JSONEncoder().encode(item)
let hash = SHA256.hash(data: raw)
let data = Array(hash)
let r = Double(data[0]) / 255.0
let g = Double(data[1]) / 255.0
let b = Double(data[2]) / 255.0
let reader = Color(
red: r,
green: g,
blue: b
)
let neonHue = reader.hsba.hue
let neonSaturation = 0.7 + (reader.hsba.saturation * 0.3)
let neonBrightness = 0.5 + (reader.hsba.brightness * 0.5)
return Color(
hue: neonHue,
saturation: neonSaturation,
brightness: neonBrightness
)
}
public static func emoji(from item: some Codable) -> String {
let raw = try! JSONEncoder().encode(item)
let hash = SHA256.hash(data: raw).intValue
let index = abs(hash) % contiguousEmoji.count
return String(contiguousEmoji[index])
}
}
extension Digest {
var bytes: [UInt8] { Array(makeIterator()) }
var data: Data { Data(bytes) }
var intValue: Int {
Int(bytes.map { String(format: "%02X", $0) }.joined(), radix: 10) ?? -1
}
}
extension Color {
var hsba: (hue: CGFloat, saturation: CGFloat, brightness: CGFloat, alpha: CGFloat) {
#if canImport(UIKit)
typealias NativeColor = UIColor
#elseif canImport(AppKit)
typealias NativeColor = NSColor
#endif
var h: CGFloat = 0
var s: CGFloat = 0
var b: CGFloat = 0
var a: CGFloat = 0
NativeColor(self)
.getHue(&h, saturation: &s, brightness: &b, alpha: &a)
return (h, s, b, a)
}
}
fileprivate let contiguousEmoji: [UnicodeScalar] = {
let ranges: [ClosedRange<Int>] = [
0x1f600...0x1f64f,
0x1f680...0x1f6c5,
0x1f6cb...0x1f6d2,
0x1f6e0...0x1f6e5,
0x1f6f3...0x1f6fa,
0x1f7e0...0x1f7eb,
0x1f90d...0x1f93a,
0x1f93c...0x1f945,
0x1f947...0x1f971,
0x1f973...0x1f976,
0x1f97a...0x1f9a2,
0x1f9a5...0x1f9aa,
0x1f9ae...0x1f9ca,
0x1f9cd...0x1f9ff,
0x1fa70...0x1fa73,
0x1fa78...0x1fa7a,
0x1fa80...0x1fa82,
0x1fa90...0x1fa95,
]
return ranges.reduce([], +).map { return UnicodeScalar($0)! }
}()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment