- JavaScript 60%
- Python 27%
- CSS 8%
- HTML 5%
- Docker <1%
bs-farm #
An isometric idle-farm visualization of a Bluesky user's activity and social graph. Type in a handle, pick some neighbors, and watch their last few months of posting grow into a tile-based landscape — each user gets a crop, every post sprouts a cell, replies and reposts grow toward the people they engage with, and tiles decay the further they get from the root.
Live at farm.cee.wtf.

Cell rules #
- Posts sprout a fresh tile on your farm.
- Replies & reposts create a cell on your farm in the direction of the recipient.
- Likes bump the maturity of the tile they're on — and every ancestor up the reply chain.
- Tiles decay the further they sit from a farm's root. Keep posting to keep growing.
- Each user gets one crop type hashed from their handle. Tile colors come from their avatar.
Run it locally #
pip install -r requirements.txt
python server.py # http://localhost:8920
python server.py --port 9000
Static files (everything under static/) are served live — edit JS/CSS/HTML and hard-reload (Ctrl+Shift+R). Restart is only needed when sim/rules.py or sim/world.py change (those are what /api/config returns).
What's in the box #
static/— the actual app. 13 JS files, no bundler. Loaded in dependency order fromindex.html.sim/,fetcher/— Python sim + repo fetcher. Mostly vestigial; the shipped app does sim + fetch entirely in the browser. Kept around for offline experiments.server.py,wsgi.py— Flask shell. Serves/api/config(default tunables) + static files. That's the critical path.design.md— the original design doc. Read it for the "why" behind the mechanics.CLAUDE.md— architecture notes for AI assistants (also useful for humans).
Tech #
Pure-browser sim + fetch. Talks to Bluesky's public AppView + per-user PDS directly via com.atproto.repo.listRecords, caches parsed events per-DID in IndexedDB, then runs the replay in a vanilla canvas with a pre-rendered sprite cache. The Flask side mostly just hands out defaults.