~ knotless Search/
↑↓ move↵ open⇧↵ all results

grain.social/grain

photography-focused photo sharing platform

grain.social
1 collaborator
0
2
60 lines · 2.4 KB Raw History

For You Feed Design

Collaborative filtering feed for Grain, reverse-engineered from the Bluesky For You feed playground by spacecowboy17.

Algorithm

Personalized mode (user has favorites)

  1. Seed: Get user's 500 most recent favorites
  2. Co-likers: Find other users who also favorited those same galleries
  3. Candidates: Collect galleries those co-likers favorited (excluding user's own favorites and own galleries)
  4. Score each candidate:
For each path (co-liker → candidate gallery):
  path_score = 1 / (co_liker_total_likes ^ divisor_power)
  path_score *= (1 - corater_decay) ^ (items_from_this_corater - 1)

base_score = sum(path_scores)
smoothed = base_score * (num_paths ^ smoothing_factor)
time_factor = 0.5 ^ (age_hours / half_life)    // half_life=0 disables decay
final_score = smoothed * time_factor / (total_favorites ^ popularity_penalty)
  1. Rank by final_score descending, paginate with cursor

Cold start fallback (no favorites)

Return galleries ordered by favorite count descending, filtered to last 30 days.

Default Parameters

ParameterDefaultDescription
half_life6Hours; demotes older posts
smoothing_factor0.5Boosts multi-path posts
popularity_penalty0.3Demotes generally popular posts
divisor_power1.0Divides by co-liker's total likes
corater_decay0Decay for same co-liker (0=none)
time_shift24Hours; co-like contribution window
seed_limit500Max seed favorites

Files

  • server/feeds/foryou.ts — feed generator with scoring logic
  • app/lib/queries.ts — add forYouFeedQuery
  • app/routes/feeds/for-you/+page.ts — page load
  • app/routes/feeds/for-you/+page.svelte — feed UI
  • app/lib/preferences.ts — add to DEFAULT_PINNED

Pagination

Score-based cursor: encode score:rkey as cursor string. Since scores are computed in JS (not SQL), fetch all candidates, score, sort, then slice by cursor position. For v1, use offset-based cursor to keep it simple.

Filters

  • Exclude galleries user already favorited
  • Exclude user's own galleries
  • Respect takedown status and hidden labels
  • Require gallery to have at least 1 item