HackerTube and Bun: a YouTube built on HN curation
You watch a video about Postgres and, the next day, YouTube offers you "10 frameworks that will die in 2027". HackerTube starts from that annoyance and gives it a simple answer: a YouTube built from videos recommended by the Hacker News community. The idea appears on Andrej Karpathy's blog, and the project details are there. Here I want to talk about what it teaches and how to build your own version with Bun.
What is HackerTube?
The concept fits in one sentence: instead of a model deciding what you see, the people who post, vote and comment on HN decide.
Notice what changes. The video stays on YouTube. The player stays on YouTube. Only the discovery layer was replaced.
And that's the interesting part. We tend to treat "the feed" as if it were the whole product. It isn't. The feed is a query, and a query can be rewritten.
The feed is just a query. And a query can be rewritten.
Why Hacker News curation matters to programmers
The YouTube algorithm optimizes for what it can measure, like clicks and screen time. That makes sense for its business, but screen time isn't the same as learning.
HN offers a different signal. Someone thought the video was good enough to post. Other people voted. Then a lot of people discussed it. A 50-minute talk about memory allocators has a real chance of rising there, even without an open-mouth thumbnail.
There's another gain I think is underrated: the signal is readable. You see the points and open the comments. Often the discussion is worth as much as the video, because someone who worked on the subject shows up to correct the speaker.
Now, let's not romanticize it. HN has its flaws:
- It has a strong bias: English, Silicon Valley, startups, Rust and nostalgia for old computers.
- A vote doesn't prove the person watched. Many people vote on the title.
- Points depend on timing and luck. A great video can die in the new queue.
In other words, you trade an opaque bias for a visible one. I'll take the visible one, because you can compensate for a bias you can see.
How to build your version with Bun
What follows is my version, not the original project's implementation. I chose Bun for an unglamorous reason: it runs TypeScript directly, ships with fetch, an HTTP server and SQLite, and needs no configuration. For an afternoon project, that's what matters.
The public HN search API, maintained by Algolia, doesn't ask for a key. You can filter stories by URL and score:
// collect.ts
const url = new URL("https://hn.algolia.com/api/v1/search");
url.searchParams.set("query", "youtube.com");
url.searchParams.set("restrictSearchableAttributes", "url");
url.searchParams.set("tags", "story");
url.searchParams.set("numericFilters", "points>=100");
url.searchParams.set("hitsPerPage", "200");
function extractId(link: string): string | null {
if (!URL.canParse(link)) return null;
const u = new URL(link);
if (u.hostname === "youtu.be") return u.pathname.slice(1) || null;
return u.searchParams.get("v");
}
const { hits } = await fetch(url).then((r) => r.json());
const videos = hits.flatMap((h: any) => {
const id = extractId(h.url ?? "");
if (!id) return [];
return [{
id,
title: h.title,
points: h.points,
comments: h.num_comments,
createdAt: h.created_at_i,
hn: `https://news.ycombinator.com/item?id=${h.objectID}`,
}];
});
await Bun.write("videos.json", JSON.stringify(videos, null, 2));
Run it with bun collect.ts and that's it, you have a JSON file.
Three traps show up right away:
- Short links: searching for
youtube.comdoesn't catchyoutu.be. Run a second query and merge the results. - Duplicates: the same video gets posted several times over the years. Group by
idand add up the points. - Pagination: the search doesn't return the entire history at once. Slice it by period, filtering
created_at_iinnumericFilters.
Ranking: points alone aren't enough
If you sort by points alone, the top turns into a museum. The same classics stay there forever.
You can steal the idea from HN itself: a formula that penalizes age. For a video archive, though, the penalty needs to be much lighter, because a good talk doesn't spoil like news:
function score(v: { points: number; comments: number; createdAt: number }) {
const years = (Date.now() / 1000 - v.createdAt) / (365 * 24 * 3600);
const engagement = v.points + v.comments * 0.5;
return engagement / Math.pow(years + 2, 0.5);
}
The weights here are a guess, and I say that without shame. A comment is worth half a point because it signals discussion, but it can also signal a fight. The 0.5 exponent just keeps 2012 from dominating the page. Tune it by looking at the result, not a spreadsheet.
That's the best side effect of having your own feed: the recommendation rule becomes ten lines you can read, change and version in Git. Try doing that with the YouTube algorithm.
Serving it all: do you need a backend?
In most cases, no. A videos.json generated by a cron job and a static page solve it. If you want a local server, Bun does it with no dependencies:
// server.ts
const videos = await Bun.file("videos.json").json();
Bun.serve({
port: 3000,
fetch() {
const cards = videos.map((v: any) => `
<article>
<a href="https://www.youtube.com/watch?v=${v.id}">
<img loading="lazy" alt=""
src="https://img.youtube.com/vi/${v.id}/hqdefault.jpg">
</a>
<h2>${Bun.escapeHTML(v.title)}</h2>
<a href="${v.hn}">${v.points} points, ${v.comments} comments</a>
</article>`);
return new Response(cards.join(""), {
headers: { "Content-Type": "text/html; charset=utf-8" },
});
},
});
Note the Bun.escapeHTML on the title. That content comes from the internet, so never trust it when building HTML. Even a toy project deserves this care.
If the experiment grows, with topic filters or a "watched" marker, bun:sqlite comes in without you installing anything. Until then, it's a JSON collection and happiness.
One caution: don't download videos, don't host videos and don't scrape YouTube. Link to them or use the official embedded player. That way the creator still gets the view, and your project doesn't become a terms of service headache.
What this changes in your workflow
Nobody is going to quit YouTube because of this. Neither am I. The real gain is something else.
First, you get a better study queue. Instead of opening the app and accepting whatever comes, you open a list filtered by people who care about the subject.
Second, the pattern works for other sources. Swap HN for Lobsters, your team's Slack links or a newsletter you subscribe to. The design is the same: collect links, extract signal, rank, display.
Third, the barrier to entry has dropped. A personal tool like this takes under 100 lines. With a coding assistant, it's done in an afternoon. Software made for a single user, yourself, finally pays off.
My take on HackerTube
I like HackerTube for what it questions, more than for what it is. The recommendations we consume every day were designed for another company's goal. Replacing that layer doesn't take a new product or a machine learning team. It takes a public API, a JSON file and a handful of your own decisions.
This won't fix the internet. It will just spare you from "10 frameworks that will die". For an afternoon of work, that's already a great deal. If you enjoy this kind of small, useful tool, there's more like it in my projects.
LinkedIn summary
YouTube shows you a video about Postgres and, the next day, offers you "10 frameworks that will die in 2027". The feed looks like the whole product, but it's just a query. And a query can be rewritten. That's what caught my attention about HackerTube, an idea that appeared on Andrej Karpathy's blog: what you see is decided by the people who post, vote and discuss on Hacker News, not by a model optimized for screen time. HN has its flaws, of course. But I prefer a bias I can see to a hidden one, because you can compensate for the visible kind. I built my own version with Bun: a public API, a JSON file and a ten-line ranking that I can read, change and version in Git. Under 100 lines, one afternoon of work. I wrote the step by step with the full code in the article. If you also want to take back control of what you watch, it's worth a read. #Bun #TypeScript #HackerNews #Programming #SoftwareDevelopment