How I built this
LetterJumpd answers one thing: how are any two films connected? Pick a start and an end, and it finds the shortest chain between them, where every hop is a person both films share. Below is the actual build. The data, the graph, the calls that change what you see, and everything that broke on the way.
Chapter 01
The idea
There's a trend where people race to link two films from memory. Start here, jump through a shared face, land there. Kal Ho Naa Ho to The Wolf of Wall Street in three: Shah Rukh Khan carries you into another Bollywood film, Amitabh Bachchan carries you into The Great Gatsby, DiCaprio takes you home. It's a good game and a terrible thing to verify. The claim is the fun part. The proof is the part nobody has.
That gap is the product. Oracle of Bacon has solved actor-to-actor for decades, and there are daily puzzle games where the film pair gets chosen for you. But nobody lets you pick any two films and watch the route assemble itself. So: two boxes, a jump, and a chain that fills the gap one hop at a time.
I decided early that the reveal was the whole thing. If the answer just appeared as a list, none of the rest would matter. You'd read it once and forget it. Everything underneath exists to make that one moment fast enough and true enough to be worth watching.
Chapter 02
Where the data comes from
All of it is TMDB, the same source Letterboxd itself uses. I never scraped Letterboxd and won't. Their terms say no, and the trend is the inspiration here, not the dataset. Everything you see, down to the posters, comes through TMDB's public API with attribution shown wherever the data is.
A hop, meaning one step in the chain, needs a person both films share. So the useful unit is the credit: this person, on this film, in this role. I keep five kinds (cast, director, composer, cinematographer, writer) and drop the rest. Producers were a deliberate cut. They're listed in their dozens on big films and connect everything to everything, which makes for technically valid chains that tell you nothing about the movies.
TMDB's terms ended up shaping this build more than any design call did. Nothing can be cached beyond six months, so syncing “what changed today” isn't enough on its own. A film nobody edits would just sit here forever, quietly going stale and out of compliance. So a scheduled job runs every night. It pulls new titles, re-fetches whatever TMDB reports as changed, and re-pulls a rolling slice of the whole catalogue, sized so every row gets renewed well inside that window. The same terms rule out AI features built on their data, so there's no chatbot bolted to the side of this, and there won't be.
Chapter 03
Crawling a million films
I started with the 20,000 most popular films just to get something running. That exposed a bias immediately: ranking by popularity buries older Hindi cinema. Kal Ho Naa Ho sits somewhere around position thirty thousand, so my own test case wasn't in my own dataset. The seed was scaffolding, not the product.
So, the full crawl. 14.4 hours, 1.17M requests, one call per film with its credits attached, run at about 25 a second. That sits well under TMDB's rate limit, the cap they put on how many requests one client may fire per second, because there was no reason to crowd them. TMDB hit two rough patches overnight and errored on roughly 1,900 films. A second pass cleared every one in 90 seconds, and that's the part I'm actually pleased with. The crawler keeps its work queue in the database, so a failure is just a row waiting to be retried. It trusts the queue, not its own memory.
I tested that by killing the process outright mid-run. The database came back consistent, the restart re-fetched only what was still pending, and nothing duplicated. Which turned out to be lucky rehearsal, because my laptop ran itself down to 9% battery in the middle of the real crawl. Same design, same shrug, instead of starting over.
1.18M
films
2.97M
people
10.3M
credits
10.5k
Hindi films
Chapter 04
The graph, and why it’s fast
Films and people form one bipartite graph. Bipartite meaning every node is either a film or a person, and every link runs between the two kinds, never film to film. There are 9.6M links, each carrying a bitmask: a handful of bits recording which roles apply to that pair, so one link can say “cast here, director there” at once. That mask is what makes the toggles instant. Turning composer on doesn't rebuild anything, it just widens the set of links the search may walk.
The whole thing compiles into flat arrays, one long run of numbers rather than a million little objects, which the server memory-maps at startup. That means it points at the file on disk and lets the operating system pull in pages as they get touched, instead of loading 171 MB into memory up front. Answering queries costs about 250 MB even though building the artifact peaks past 2 GB.
A query runs a bidirectional search. It explores outward from the start film and the end film at the same time, always expanding whichever side currently has fewer nodes to look at. That matters more than it sounds. A hub actor can carry hundreds of films, so searching from one end alone drowns in them, while two searches meet in the middle after a fraction of the work. Where they touch gives the exact shortest length.
From there it doesn't stop at one answer. It reconstructs the layer of routes that share that length, counts them exactly, and ranks them. So the header says “1 of 63 shortest routes” and means precisely 63, not an estimate. Ties break the same way every time, which is the boring detail that makes a shared link replay the same route for whoever opens it. Half of all queries answer inside 6 ms, and 95% inside 29 ms.
That's a real slice of the graph above, not an illustration: 52 well-known films and the 70 people who link at least two of them, pulled straight from the database and laid out in three dimensions. Spin it, or grab a dot and pull. The full thing is the same shape, roughly eighty thousand times over.
One number I didn't expect: 42% of random film pairs have no connection at all, at any depth. I checked whether the hop limit was hiding routes. It wasn't. They're obscure titles whose handful of credited people never worked anywhere else, little islands floating off the main mass of cinema. For films people have actually heard of, it's under 1%.
Chapter 05
Search was the slow part
Routes were easy. Finding the film you meant was not. “Kal Ho Na Ho” has to land on “Kal Ho Naa Ho”, because transliteration doesn't agree with itself and neither do the people typing it. So search matches on trigrams, every overlapping run of three characters in a title, so “gatsby” becomes gat, ats, tsb, sby. That tolerates a missing or doubled letter which whole-word matching would choke on. The database keeps an index of those triples, a lookup table so it never has to read every title. Across 1.18M films that still cost up to 600 ms, which is unusable for something meant to answer while you type.
Fixed it in two moves. First a ladder, cheapest rung first. An exact substring match answers most queries in single-digit milliseconds, then word matching catches things like “spider man” finding Spider-Man, and only a genuine misspelling falls through to the fuzzy pass. My first attempt still ran fuzzy even after an exact hit: 365 ms wasted confirming something already correct.
Second, a separate index over the 250,000 best-known films for that fuzzy pass to try before the full catalogue. The logic is simple. People misspell films they've heard of. Worst case went from 610 ms to 130 ms, and “interstelar”, “godfater” and “kabi kushi kabhi gam” all land where they should.
Chapter 06
Decisions that change the answer
Bit parts are off by default. The first honest answer between Kal Ho Naa Ho and The Wolf of Wall Street was two hops, through an uncredited extra listed as “Bagel Vendor”. Both films shot in New York and used the same local crowd. Technically the shortest, completely useless. Cast billed below tenth and anything marked uncredited now sit behind a deep cuts toggle. Leave them off and the route becomes Shah Rukh Khan to Amitabh Bachchan to Leonardo DiCaprio: three hops, and the one you actually wanted. Nothing is deleted. The extras are still in the database, one switch away.
Then there was the question of how many routes to show. Every possible path between two films runs into the millions the moment a hub actor shows up, and means nothing. Every shortest path is a small, countable set that falls out of the same search for free. So that's what “find all connections” means here.
Ranking leans toward films you'd recognise, because a chain is only satisfying if you know the faces in it, so routes get scored by how well-known their films and people are. It isn't perfect yet. The Hollywood-heavy route still edges out the Shah Rukh one on the default sort, and I want to tune that.
Synopses get fetched on demand. Storing a plot summary for a million films meant another 14-hour crawl for text almost nobody reads. So they get pulled for whatever's on your screen the moment a route loads, then cached. The first look costs a network trip. Every look after that is instant.
Chapter 07
Making it feel like something
A correct answer rendered as a list is forgettable, so the route arrives as choreography. The two films you picked glide apart to make room, then each hop draws its wire and pops the next film into place, about two seconds end to end for a four-hop chain. Everything eases out rather than in, and nothing bounces. The pop is a 3% overshoot, enough to feel alive and not enough to feel like a toy.
Long chains used to run off the side of the screen behind a scrollbar, which is a miserable way to read a route. Now it zig-zags, ends on a baseline and middles alternating above and below, and the layout picks how wide to run from your viewport, with zoom and pan for the long ones. The labels sit above the wires instead of across them, because at four hops the pills were covering the very thing they were labelling.
Hover any film for its poster, runtime, director, top cast and synopsis, plus links out to Letterboxd and TMDB. And if you have reduced motion turned on, which is the accessibility setting that tells sites to stop animating, you get exactly the same information with none of the movement. The reveal is a nice-to-have. The answer isn't.
Chapter 08
What broke
A rate limiter that hung forever, because it waited for a counter to reach exactly 1.0, and floating point (the way computers approximate decimals) never quite lands on a round number. It surfaced as a test that simply never finished. No error, no output, just a process sitting there.
A hot swap that compared folder names. Hot swap here means replacing the graph a running server is using without restarting it, so nobody sees an outage. Because it compared names, rebuilding twice in one day changed nothing, which is exactly what a manual re-run does. I fixed it with a build timestamp, then had to move that to nanosecond precision, because two rebuilds inside the same second looked identical too.
A “previous route” button that wrapped from route 1 back to route 1,350 and got a 422 back, which is the API's way of saying the request was out of range, since it caps how deep you can page. A logo whose letterforms merged into a plain “U”. Hover cards sliding off the canvas edge. And macOS quietly marking a Python import file as hidden, which Python 3.13 then refused to read. That one cost an hour and had nothing to do with this project at all.
33 journal entries, one per milestone, bug and decision. I kept that log honest while building rather than tidying it afterwards, which is the only reason I can write this chapter accurately.
Chapter 09
What’s next
The branching view is the big one: every shortest route at once, as a web instead of a single line. The API already returns it, counted and ranked, so it's a drawing problem, not a data problem.
After that, a share image per route so a link posted anywhere carries its own chain, and IMDb alongside the Letterboxd and TMDB links. Then race mode, which was in the first sketch and got deferred. You guess the chain, then the answer reveals and tells you how close you were.
And a rate limit before I share the link widely, because right now the API is open and unauthenticated. The catalogue keeps refreshing nightly either way.
Chapter 10
Colophon
This product uses TMDB and the TMDB APIs but is not endorsed, certified, or otherwise approved by TMDB. Inspired by the Letterboxd speedrun trend; not affiliated with Letterboxd.
← Back to the jump