Something went wrong. Try again.
grab primary colors from a bsky pfp
Something went wrong. Try again.
Files
- Python 98%
- Docker 2%
README.md
bsky-primary #
A tiny FastAPI service that extracts two prominent colors from a Bluesky
user's avatar. Point it at one or more handles/DIDs and get back a light
and dark hex color per actor — handy for theming UI to match someone's
profile picture.
API #
POST /colors #
{ "actors": ["@alice.bsky.social", "did:plc:xxxx", "bob.example.com"] }
Response — keyed by the exact strings you sent:
{
"@alice.bsky.social": { "light": "#e2c48f", "dark": "#5a3b21" },
"did:plc:xxxx": { "light": "#88c0d0", "dark": "#2e3440" },
"bob.example.com": null
}
- The two colors are sorted by luminance;
darkis suitable for strokes/text. - A value is
nullwhen the avatar can't be fetched, decoded, or yields no usable color — callers should fall back to their own defaults. - Up to 60 actors per request. Results are cached in-memory for 24h (failures retried after 5 min).
GET /colors/{actor} #
Convenience lookup for a single handle or DID. Add ?refresh=true to bypass
the cache and re-fetch the avatar.
GET /colors/@bsky.app
GET /colors/did:plc:z72i7hdynmk6r22z27h6tvur?refresh=true
{ "light": "#e2c48f", "dark": "#5a3b21" }
Returns null if the actor can't be resolved or yields no usable color.
GET /healthz #
{ "ok": true, "cache_size": 12 }
OpenAPI #
A static spec lives at openapi.json (OpenAPI 3.1). The
running service also serves it live at /openapi.json, with interactive docs
at /docs.
How it works #
- Resolve avatars in batches via
app.bsky.actor.getProfileson the public Bluesky API (no auth required). - Fetch each avatar (5 MB / 5 s caps), resample to 32×32.
- Histogram pixels into 4-bit-per-channel buckets, skipping near-black, near-white, and transparent pixels.
- Pick the two most-populous buckets at least
MIN_COLOR_DISTapart, average each bucket, and sort by luminance.
Tuning constants live at the top of main.py.
Running #
Local #
pip install -r requirements.txt
uvicorn main:app --host 0.0.0.0 --port 8000
Docker #
cd deploy
# optional: cp .env.example .env and set HOST_PORT
docker compose up --build -d
The container listens on 8000; the compose file publishes it on
HOST_PORT (default 8000).
Example #
curl -s localhost:8000/colors \
-H 'content-type: application/json' \
-d '{"actors":["@bsky.app"]}'