Inspiration

We kept noticing the same thing: people who would never sit down to read a history
book will spend two hours doom-scrolling reels. The format is the drug — infinite
scroll, likes, social proof, bite-sized posts. Wikipedia has millions of extraordinary
human stories. The problem isn't the content. It's the delivery mechanism.

We asked: what if the scroll was the point, but the content was worth something? What if Marie Curie had an Instagram — her actual 1920 portrait as the profile photo —
and you actually learned something while wasting time?

## What it does

LearnScroll is a social media feed where every post is a real historical moment, narrated by the historical figure who lived it.

  • Year-range explorer — a timeline slider on the landing page lets you pick any era in history and dive in. Want just the 1860s? Drag the handles. Hit Explore. The feed rebuilds instantly with posts from that exact period.
  • Infinite feed of Wikipedia-sourced posts from real historical figures — with their actual Wikimedia Commons portrait photos, precise historical dates, images, and emotionally charged first-person narration
  • Topic chips — one-click exploration by theme (abolition, physics, ancient Rome) alongside the year range, so you can filter by both era and subject
  • "On This Day" card — surfaces a historically relevant post matched to today's calendar date; clicking it zooms the feed to that exact year
  • Real social layer — create an account, like posts, leave comments, hover a like count to see exactly who liked it. Nothing is faked or mocked.
  • AI enrichment pipeline — drop a Wikipedia article into a watched directory; within seconds it's extracted, synthesized, and published as a live feed post

The UX is indistinguishable from Instagram. The content is Harriet Tubman describing the Underground Railroad in first person.

## How we built it

Frontend: React + TypeScript + Tailwind CSS (Vite). Infinite scroll via IntersectionObserver with cursor-based pagination. Year-range slider drives client-side filtering so era changes feel instant. All social actions (likes, comments, auth) call real API endpoints with real database persistence.

Backend: FastAPI + SQLite (WAL mode). Versioned SQL migration system. Cursor-based feed pagination so new posts never shift offsets. SHA-256 + random salt password hashing for auth. Background worker co-located with the HTTP server via asyncio, elected via filesystem lock so only one process runs workers.

AI pipeline — two stages:

  1. Extraction (gpt-5.6-luna) reads a raw Wikipedia article and produces a structured ArticleDossier: key facts with unique IDs, named entities, time periods, human stakes, emotional dimensions, political and social dimensions, narrative material, engagement signals, and sensitivity flags.

  2. Synthesis + post generation (gpt-5.6-terra) takes the dossier and writes one emotionally charged social post — punchy title, 25–45 word body, first-person voice of the central figure, dominant emotion (anger / grief / awe / disbelief), and a precise historical date. Every factual claim is grounded in verified evidence IDs from the dossier; hallucinated evidence IDs are rejected at runtime.

All LLM calls are logged to SQLite with full token counts, latency, and estimated cost — so we can audit exactly what the pipeline spent and why.

Historical date precision: we built a five-level system (year / month / day / range / circa) so "c. 1489" and "2 September 31 BCE" and "1895–1898" all render correctly without being flattened to a year.

## Challenges we ran into

Getting the right content. Our first pass pulled random Wikipedia articles and the feed filled with physics papers and niche science stubs. That's a textbook, not a history feed. We had to build deliberate curation: specific historical figures, thematic groups, controlled topic taxonomy. The AI is only as good as what you feed it.

Keeping social features real. It was tempting to fake likes with random numbers. We didn't — real users, real rows, real auth. That meant properly hashing passwords across seed runs (deterministic salts so demo accounts survive any database reset), debugging why "invalid credentials" kept firing even with the right password.

Parallel development under time pressure. Two engineers pushing to the same
branch meant frequent rebase conflicts on the main component file. We established a discipline — fetch, rebase, stash-pop, push — and held to it even late at night.

Schema stability across AI calls. The enrichment output schema has ~15 nested types. A single renamed field breaks the feed silently. We invested in strict Pydantic validation (extra="forbid") and evidence ID cross-checking so bad outputs are rejected loudly, not silently swallowed.

Business model design. We wanted the engagement mechanics to support real monetization — not just a demo. We studied how Facebook ties attention to ad targeting and built the social layer with that intent: the interest graph (topics liked, eras explored, figures followed) is a natural signal for relevant ads from museums, online courses, publishers, and history-adjacent brands.

## Accomplishments that we're proud of

  • Wikipedia portrait photos as profile avatars. When Marie Curie's actual photograph appears next to her post about discovering radium, it lands differently than a generic icon. That one detail makes the whole thing feel alive.
  • A two-stage AI enrichment pipeline that goes from raw Wikipedia JSON to a live feed post — with full evidence grounding, cost tracking per LLM call, and runtime rejection of hallucinated claims.
  • A fully working social platform — not a prototype. Real auth, real persistence, real cursor-based pagination, real "who liked this" hover popover.
  • Emotionally engineered content. The post generation prompt picks a dominant emotion from anger / grief / awe / disbelief and makes every sentence intensify it. Engagement goes up when content provokes feeling — we use that mechanism for good.
  • Year-range exploration as the primary UI. Instead of a search box, users drag a timeline slider. It's more tactile, more visual, and makes the breadth of history
    feel navigable rather than overwhelming.

## What we learned

The format is more powerful than the content. Wikipedia has been free and comprehensive for decades. We changed how it's delivered, and suddenly it feels engaging. That's both exciting and a little alarming — and it's exactly what makes this worth building.

Technically: two-stage LLM pipelines (cheap model for extraction, stronger model for synthesis) are significantly cheaper and more reliable than one large prompt. SQLite with WAL mode handles concurrent API + background worker cleanly. Cursor-based pagination is the only sane default for feeds.

On AI output: invest in the schema design first, not the prompt. Strict Pydantic models and evidence ID validation do more to prevent hallucinations than clever prompting ever could.

## What's next for LearnScroll

  • Personalized feed — the single biggest unlock. Every like, comment, and era
    explored is a signal. We feed those signals back into ranking so the feed surfaces more of what each user actually engages with — the same loop that makes Instagram impossible to put down, applied to history. A user who liked three posts about the Roman Empire should never see a generic feed again.
  • Interest-based advertising — the Facebook model, applied to education. Museums, universities, publishers, and documentary platforms can reach users by the exact eras and topics they engage with. Zero creepy tracking required — the interest graph is right there in the engagement data.
  • Real-time Wikipedia ingestion — automated pipeline monitoring Wikipedia's featured articles and "On This Day" API; no manual drops required
  • More historical figures — expand to hundreds of characters across civilizations, with community-suggested additions
  • Study groups — teachers share a curated group with students; the backend already supports groups and group-filtered feeds
  • Mobile app — the scroll paradigm was born on mobile; a native app is the natural next step

Built With

Share this project:

Updates