Wikifreedia
All versions

A deterministic method for deriving a visually consistent, UI-optimized color from any string. Designed to produce colors that read well as text, backgrounds, borders, and indicator dots across dark, light, sepia, ivory, and grey themes. Simpler and more perceptually balanced than hash-RGB approaches (e.g. XEP-0392). # String Color Any UTF-8 string can be mapped to a unique color via a polynomial character-code hash. ## Hashing 1. Normalize the input: trim() and toUpperCase() 2. Return a neutral grey rgb(128, 128, 128) for empty strings 3. Compute a BigInt by summing character codes weighted by powers of 256: number = Σ ( charCode(i) × 256ⁱ ) for i in [0, len) 4. Derive hue: hue = number mod 360 ## HSV Parameters S = 0.70 V = 0.70 if 32 ≤ hue ≤ 204 (warm yellows, greens, cyans — perceptually bright) V = 0.96 if 216 ≤ hue ≤ 273 (blues — perceptually dark, need boosting) V = 0.90 otherwise (reds, magentas, warm blues) ## HSV → RGB Conversion Standard HSV-to-RGB via intermediate chroma values: h = hue / 60 c = V × S x = c × (1 − |( h mod 2 ) − 1|) m = V − c Sector assignment (h in [0,6)): | h range | R | G | B | |———|––|––|—–| | [0, 1) | c | x | 0 | | [1, 2) | x | c | 0 | | [2, 3) | 0 | c | x | | [3, 4) | 0 | x | c | | [4, 5) | x | 0 | c | | [5, 6) | c | 0 | x | Final output: R, G, B = round((r + m) × 255) for each channel. # Hex String Color When the input is a hexadecimal string (e.g. a pubkey of a Nostr Profile ), parse it directly as a BigInt rather than hashing character codes: number = BigInt.parse(hex, radix: 16) hue = number mod 360 Then apply the same HSV conversion with one difference — V for the warm/green/cyan range is 0.75 instead of 0.70, which accounts for the higher average luminance of hex-encoded keys in that range: V = 0.75 if 32 ≤ hue ≤ 204 V = 0.96 if 216 ≤ hue ≤ 273 V = 0.90 otherwise # Text Readability Adjustment When rendering the derived color as text (author names, mentions, etc.), apply a small brightness correction to the RGB output: - Dark mode: multiply each channel by 1.08 (+8%) - Light mode: multiply each channel by 0.95 (−5%) Clamp each channel to [0, 255]. # Client Recommendations - MUST normalize strings to trimmed uppercase before hashing - MUST use BigInt arithmetic for the polynomial hash to avoid integer overflow - SHOULD apply the text adjustment when rendering colored names or mentions - MAY skip the text adjustment for non-text uses (backgrounds, borders, avatars) - SHOULD fall back to rgb(128, 128, 128) for empty or invalid inputs

See Also

Other authors

No one else has published this topic yet.