Viral score

Every video gets a 50–99 score computed in src/lib/viral.ts. It blends engagement quality with posting velocity, then normalizes within the scraped batch.

The formula

engagement = (likes + 4·comments + 6·shares + 3·collects) / views
velocity   = views / hours_since_posted

rawScore = 0.55 · ln(1 + engagement·1000)
         + 0.45 · ln(1 + velocity/1000)

viral_score = 50 + 49 · (rawScore − batchMin) / (batchMax − batchMin)

Why these weights

  • Shares ×6 — the strongest algorithmic signal on every platform.
  • Comments ×4 — conversation drives distribution more than passive likes.
  • Collects ×3 — saves signal durable value.
  • 55/45 split — engagement quality slightly outweighs raw reach, so small-but-brilliant videos can outrank big-but-empty ones.

Edge cases

  • Hidden view counts — Instagram often hides plays. When views are 0, reach falls back to likes × 10 and engagement switches to a comment-rate-based proxy.
  • Freshness — velocity divides by hours since posting (minimum 1h), so a 3-hour-old video with 1M views outscores a 3-month-old one with the same numbers.
  • Batch normalization — scores are relative within each scrape batch, mapped to 50–99. Cross-platform comparison stays meaningful because Analyze scores a video against all peers of the same platform in your DB.

Score bands

  • 95–99 — exceptional, top-of-feed material
  • 85–94 — strong, proven patterns worth stealing
  • 70–84 — solid performers
  • 50–69 — trending, but unremarkable