Dynamic avatar rotation
0

Configure Feed

Select the types of activity you want to include in your feed.

JavaScript 94.5%
Shell 3.5%
Dockerfile 1.9%
9 1 0

Clone this repository

https://git.vm.fail/gui.do/bsky-avatar https://git.vm.fail/did:plc:jyxjaxragtqzuhf5u453luq4
ssh://git@knot1.tangled.sh:2222/gui.do/bsky-avatar ssh://git@knot1.tangled.sh:2222/did:plc:jyxjaxragtqzuhf5u453luq4

For self-hosted knots, clone URLs may differ based on your setup.


README.md

bsky-avatar#

My Bluesky avatar updates itself. It changes with the weather where my phone is, the season, the date, and wherever I'm travelling, and I can pin a custom one by hand for any stretch of time (a holiday, a heads-down week).

It stacks hand-drawn transparent layers over a fixed portrait of me (the avatar-system design) and pushes the result to gui.do on Bluesky, and optionally mirrors it to Signal. It runs on a schedule a few times a day, and every run is idempotent, so a missed run or an extra one does no harm.

Design, internals, and the decisions behind it: ARCHITECTURE.md.

How it decides#

Each run builds a small state object, { bg, weather, season, holiday, ring, halo }, and renders it in a fixed z-order (ported straight from the design's studio.html). Each field resolves on its own, with a manual override winning first:

Field Source
bg manual override, else the country flag where my phone is, else a date background (e.g. Pride), else none
weather Open-Meteo current weather for the phone's coordinates, mapped to one of 6 layers
season the date (dropped while a flag is up)
holiday the date (config/holidays.yaml: Sinterklaas, King's Day, NYE, birthday, …)
ring daily colour rotation at home, neutral while abroad
halo on only when a flag background is showing

Where my phone is#

Coordinates come from a Home Assistant person entity, rounded to about a kilometre before they leave the LAN, then reverse-geocoded to a country through OpenStreetMap Nominatim. If I'm not home, that country's flag fills the background. Every external call fails soft: if HA, Nominatim, or Open-Meteo is down, it falls back to home coordinates or the last sensible value instead of crashing.

Configure#

The repo ships example configs. Copy each one and edit your own. The real files are gitignored and live only on the NAS, because they hold personal things like travel dates that don't belong in a public repo.

  • config/settings.example.yamlsettings.yaml: timezone, home country/coords, HA entity, birthday, abroad ring.
  • config/holidays.example.yamlholidays.yaml: date → holiday layer, plus ranges (festive week, Pride month).
  • config/overrides.example.yamloverrides.yaml: manual periods, either a partial state or a full pre-made image.
  • .env.example.env: secrets (Bluesky app password, HA token).

Layer and flag names have to match the files in assets/layers/ (without .png).

Run#

npm install
cp .env.example .env                                   # fill in BSKY_APP_PASSWORD + HASS_TOKEN
cp config/settings.example.yaml config/settings.yaml   # same for holidays + overrides

npm run dry                 # compute + render to ./out.png, no upload
node src/index.js --state '{"bg":"flag-gr","weather":"weather-rain","ring":"none","halo":true}' --dry-run  # preview any combo
node src/index.js           # real run, uploads only if the state changed
node src/index.js --force   # upload even if nothing changed

Schedule#

It runs as a single detached, self-scheduling container (Docker): updates at 04:00, 09:00, 14:00, 18:00 and 22:00 (Amsterdam time), restarts on boot, and fails soft. Holidays show over a window from 22:00 the evening before to 04:00 the morning after (so they land across time zones); the 22:00 and 04:00 runs flip those edges. A sample container setup is in deploy/.

Why an app password#

This logs in with a Bluesky app password, a deliberate exception for a low-stakes personal toy (I use OAuth everywhere else). The secret stays in .env on the NAS and never gets committed.