Inspiration
Memetics says ideas behave like organisms: they need hosts, they spread along contact, they mutate, they die out. Everybody agrees the metaphor is nice. Almost nobody shows you the host population.
HopHacks asked us to take memetics seriously. We wanted a picture you could press play on — not a dashboard of engagement scores, not an LLM guessing “what’s trending.” Subreddits are the hosts. Words are the memes. If two communities sit close on the map, it should be because they said the same things, and you should be able to click the line and read the posts.
The question that stuck: can two communities nobody would file together still get close — and can you explain it?
What it does
Meme Drift is a vocabulary map of 512 subreddits, one snapshot a month from January 2020 to August 2026.
- Type a word (
openai,claude,potty) and watch who said it first, who caught it next, and which line carried it across. - The layout is a spring graph: more shared vocabulary, shorter line. Themes are not hand-painted; they emerge from seeded label propagation.
- Findings walk the live map — including
claudeas a GTA character in 2020 and an AI model in 2025. - Strange bedfellows is the judge question as a button: r/Parenting and r/dogs have sat next to each other for 76 of 80 months because of one word,
potty. Toddlers and puppies get trained with the same language. - Explain with Cursor is a narrator over the evidence, not an input to the graph. It answers why from themes, bridge words, mention counts, and post titles.
Every claim on screen is falsifiable: the word, the counts on both sides, and the threads.
How we built it
Three stages, on purpose. Fetch is slow and flaky. Build is a pure function. The viewer is static files.
- Sample. Arctic Shift (the public Pushshift archive): ≤100 posts per sub per month, title + lead paragraph, cached so a 40,960-request run is resumable.
- Vectorize. Each
(sub, month)is a TF-IDF document. Sublinear term frequency times inverse document frequency, then L2-normalized so cosine similarity is a dot product:
$$ \operatorname{tfidf}(t,d) = \bigl(1 + \log \operatorname{tf}_{t,d}\bigr)\,\log\frac{D}{1+\operatorname{df}_t} $$
$$ \cos(u,v) = u^\top v \quad\text{when }|u|_2=|v|_2=1 $$
- Link. Each sub connects to its $K=5$ nearest peers that month. The word on the line is $\arg\max_t\,(u \odot v)_t$ — the single term that contributes most to the pair’s similarity — plus raw mention counts so the claim is checkable.
- Colour. Twelve theme seeds; everyone else adopts the heaviest neighbouring theme until the labels stop moving.
- Play. The timeline is fractional: the layout interpolates between months, so playback glides. 2D is D3; 3D is the same spring law in three.js.
- Explain. Selection JSON goes to a small Python server that runs the Cursor SDK in an empty working directory. The agent cannot see this repo. The LLM never writes a node or an edge.
Pipeline deps are Python stdlib + NumPy. No torch. The built data/ is in the repo, so clone and serve.
What we learned
Association is not transmission. A shared bridge word means two communities were oriented toward the same framing that month — it does not prove one infected the other. The map is honest about that, and so is the explainer: it is instructed never to claim causation.
We also learned that “strongest cross-theme edge” is a terrible ranking. It surfaces spam. Persistence, a real word used in many subs, non-tautological pairs, and a human pass is what turned noise into the potty / cozy / cursed cases.
And: if the graph is the science, the model has to stay outside it. Once you let an LLM place nodes, you cannot click a line and get a receipt.
Challenges
Forty thousand fetches. Arctic Shift is public and rate-limited. We treated the network as an idempotent cache (cache/*.json) and the build as a function of that cache, so a crash never meant starting over.
The payload. 512 nodes × 80 months is a large static site. Post titles stay in per-month files and load only when you ask for receipts, so first paint is the graph, not 241 MB of threads.
Strange bedfellows. The first automated ranking linked r/Economics to r/europe with a junk token. Filters for persistence, breadth, Jaccard, and one-word dominance, then a pinned list of twelve pairs, was the only ranking that survived reading out loud.
A narrator that does not parrot itself. Cached explanations made demos fast and answers identical. We kept the cache off the default path and send a live prompt with a rotating lens, so “why are these two close?” is a short answer plus a new reason about meme evolution, not a recap of the cosine score.
Localhost vs. 127.0.0.1. The explain server originally bound IPv4 only. Browsers on localhost hit IPv6 and got “proxy unreachable.” Dual-stack bind plus fetch fallbacks fixed a bug that looked like an API outage.
Sampling is ≤100 posts per sub per month, titles and lead paragraphs only, comments out of scope. Co-occurrence is not a contact-tracing proof. We would rather show the receipts than overclaim.
Log in or sign up for Devpost to join the conversation.