The Math Behind Netflix Recommendations: Matrix Factorization

Netflix has never watched a movie, yet it knows what you'll want to watch next. Here's the linear algebra underneath the recommendation, hidden taste vectors, dot products, and a trick called matrix factorization.

By Petrus Sheya

July 21, 2026 · 6 min read

Netflix has never watched a single movie. It doesn't know what a car chase looks like, or what makes a joke land, or why an ending makes you cry.

And yet it can guess, often eerily well, exactly what you'll want to watch next.

How?

Not by understanding movies. By understanding patterns in numbers.

Here's the setup: every rating you've ever given lands in one giant table. Rows are viewers. Columns are movies. Cells are ratings, 1 to 5 stars.

Most of that table is empty. You've rated a sliver of everything on the platform, and so has everyone else.

Netflix's real problem isn't taste. It's filling in the blanks of an almost-empty table, using nothing but the handful of numbers already sitting in it.

There's a beautifully simple piece of linear algebra that does exactly this. It's called matrix factorization. Once you see it, you won't look at a recommendation the same way again.


The problem: a table mostly full of blanks

Picture a table with you and seven other viewers down the side, and eight movies across the top. Each cell holds a rating, if that person happened to watch and rate that movie.

But here's the thing: nobody watches everything. Real streaming platforms have hundreds of millions of viewers and tens of thousands of titles. Even the most active viewer has rated a sliver of a percent of what's available.

Drag the slider below and watch how sparse a realistic ratings table looks. Most of it stays blank no matter what.

Every real ratings table looks like this: rows of viewers, columns of movies, almost entirely blank. Hover a cell to see what it means.

M1M2M3M4M5M6M7M8YOUU2U3U4U5U6U7U84???2??????3????????3????????????3???????33??????1????4?????5???
Known ratings10
Blank cells54
Sparsity85%

So the question becomes: can we guess the blank cells using only the filled ones? It turns out yes, and the trick is to stop thinking about movies and start thinking about taste.


Hidden taste dimensions

Here's the intuition. Imagine every viewer has a secret taste profile, a handful of numbers describing how much they like things like explosions, slow-burn romance, dark humor, or plot twists.

And imagine every movie has a matching profile: how much explosion, romance, humor, and plot-twist it actually contains.

Nobody writes these numbers down. Netflix never asks "on a scale of 1 to 10, how much do you like explosions?" But here's the key move: if these hidden numbers existed, your rating for a movie would just be how well your profile matches its profile.

We call these hidden numbers latent factors. A taste profile, or a movie's flavor profile, is really just a list of numbers, and in math, a list of numbers is a vector.

Let's simplify to two hidden dimensions so we can actually draw it. In the toy below, each axis is a made-up taste direction. Your vector points somewhere in that space. So does the movie's.

Drag the movie's taste vector around the circle and watch the predicted rating rise and fall with alignment.

u (you)v (movie)
Δθ40°
u · v = cos(Δθ)0.76
Predicted rating4.5 / 5

Drag the movie's vector around the circle. Watch what happens to the predicted rating as it swings from pointing the same way as yours, to pointing the opposite way.

...when the two vectors line up, the predicted rating shoots up. When they point opposite directions, it craters. Alignment is the whole idea.

We measure "how aligned" two vectors are with the dot product. For vectors of the same length, it's just the cosine of the angle between them:

r^=uv=cos(Δθ)\hat{r} = \mathbf{u} \cdot \mathbf{v} = \cos(\Delta\theta)

Netflix doesn't use 2 hidden dimensions. Real systems use somewhere between 50 and 200. But the math is identical: a rating prediction is a dot product between a user vector and a movie vector.


Reverse-engineering the taste vectors

There's a catch we've been ignoring. Nobody actually knows anyone's taste vector, or any movie's flavor vector. Those are exactly the hidden numbers we started out trying to find.

So how do we get them? We guess randomly, then improve the guess, over and over, until the predictions match the ratings we already know.

For every known rating, we can measure how wrong our current guess is:

errorij=rijuivj\text{error}_{ij} = r_{ij} - \mathbf{u}_i \cdot \mathbf{v}_j

Then we nudge both vectors a little in whichever direction shrinks that error. Do this thousands of times, across every known rating, and the vectors slowly settle into values that explain the data we've already seen.

This process is called gradient descent, and it's the same basic idea used to train nearly every machine learning model that exists. Watch it happen live below.

Real gradient descent, running live. Watch the highlighted cell, nobody ever rated it, get filled in as training proceeds.

M1M2M3M4M5U1U2U3U4U50.50.50.50.50.50.50.50.50.50.50.50.50.50.50.50.50.50.50.50.50.50.50.50.50.5
Iteration0
RMSE (known cells)3.179
Predicted (never rated)1.00

Hit play and watch the numbers converge. The color behind each cell is the ground truth, the number inside is the model's current guess, and it crawls toward matching that color as training proceeds.

Notice the highlighted cell. Nobody ever rated that one. There's no ground truth for it. But because the model learned taste vectors that explain everything else, it can still predict what that rating probably would have been. That's the whole trick, running in real time.


How many hidden dimensions do you actually need?

We picked 2 hidden dimensions for the drawing, and a small handful for the training demo. But how many should a real system use?

Too few, and the model can't capture real nuance, everyone starts to look the same. Too many, and it starts memorizing noise instead of learning real taste, a problem called overfitting.

There's a sweet spot, and you can usually spot it directly in the error curve.

Slide the number of hidden taste dimensions and watch error drop then flatten, and storage cost climb.

12345678k (hidden dimensions)storage: factors vs. full table1.5× smaller
RMSE0.212
Numbers stored54 / 80
Compression1.5×

Slide the number of hidden dimensions up. Notice the error drops fast at first... then flattens out. That flattening point is the curve telling you roughly how many real hidden taste dimensions exist in this data.

And notice something else. Past a certain point, storing the factor vectors costs more numbers than just storing the original table directly. More dimensions isn't free, and it isn't always better.


From a toy table to hundreds of millions of viewers

Everything above works with an 8-by-8 table because your screen can only show so much. A real streaming ratings table has hundreds of millions of rows and tens of thousands of columns, almost all of it blank.

The math doesn't change. Same dot products, same gradient descent, same idea of nudging vectors to shrink error. It just runs at a scale that needs serious engineering, spread across many machines, updated continuously as new ratings and views stream in.

This general technique, factoring one big sparse matrix into two much smaller ones, isn't unique to movies either. It's the same idea behind song recommendations, "customers also bought" lists, and even some techniques in data compression. Spot a sparse matrix, and matrix factorization usually isn't far behind.


The short version

Netflix represents ratings as a giant, mostly-empty table. It assumes every viewer and every movie has a hidden taste vector, numbers nobody ever measures directly. A predicted rating is just the dot product of those two vectors, how well they align. Gradient descent reverse-engineers the vectors by nudging them until predictions match the ratings we already know. Once the vectors are learned, every blank cell in that giant table gets filled in for free.

Next time your recommendations feel a little too accurate, that's not magic. That's a dot product.


All visualizations are interactive React components running entirely in your browser. The factorization trainer runs real gradient descent on a small synthetic ratings table, seeded deterministically so the numbers you see are reproducible. No libraries beyond React.