به Nostr بپیوندید
2026-04-30 20:41:23 CEST
in reply to

Niel Liesmons on Nostr: Latest: ...

Latest:
https://www.chateau.community/wiki/naddr1qvzqqqrcvgpzqesd33ux28msfplvnwxac2p7988j2ctf8hdrhgjx607nczxmkuyrqqx8xarjd9hxwttrdakx7usdljg6f
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
[[nostr-profile|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
- [[nostr-profile|Profile]]

Wrote this last year too:

Colors as a fun and useful identifier

Nostr apps often use totally random colors for their default avatars, profiles names in chat bubbles, etc... That's a missed opportunity. Why don't we just we just derive a color from the npub and use that instead, interoperably across apps?

It gives us a fun and visual extra point of recognition, often without having to add anything extra to the UIs.

The only problem is that we cannot just allow for any color. The colors should be readable as text in Light and Dark modes and gray-scales should be avoided too.

This is the goal: Color Test in Light and Dark Mode Luckily, there's a super simple solution.
(which was used to derive the colors above)

Deriving the Colors

You can find the simple spec here:

TLDR: 1. Convert HEX pubkey to Int 2. Calculate the Hue value: Int % 360 3. Set Saturation to 90 for Hues between 216 and 273, use 80 for the rest 4. Set Brightness to 65 for Hues between 32 and 212, use 85 for the rest

Convert HSB color to whatever format you need. Done.

Easy & Fun to integrate!


Implementations are live in Zapstore and Wisp:
https://github.com/zapstore/webapp/blob/main/src/lib/utils/color.js

I tried this existing standard too but don't find the colors work well enough:
https://xmpp.org/extensions/xep-0392.html