Inspiration

Image generation has gotten absurdly good. Type a sentence, get a magazine cover. But it's blind. It will happily make you a gorgeous thumbnail that has nothing to do with you, your channel, your face, or the people who actually click. That was the gap we kept staring at: the models can render anything and understand nothing about context. A thumbnail isn't a pretty picture, it's a decision, and the decision depends on who the creator is and what already works for them.

So the question became simple. What if generation actually knew you? Your retention, your best formats, your niche, your real face. NextUp is our attempt to bolt context onto a context-blind model.

What it does

NextUp lives inside YouTube Studio as a native tab. It reads your channel and tells you what to make next, with a reason grounded in your own numbers, then generates an on-brand thumbnail using your actual face. Brand new channel with nothing to learn from? It asks your niche and hands you a starter slate instead of guessing from titles.

How we built it

The frontend is a Chrome extension (MV3) that injects a React app into Studio through a shadow root. The backend is FastAPI with Postgres + pgvector for retrieval, an image model for thumbnails, and an LLM for the briefs.

The grounding is RAG. We chunk the creator's videos, transcripts, comments, and niche trends, embed them, and retrieve by cosine similarity:

$$\text{sim}(q, c) = \frac{q \cdot c}{\lVert q \rVert \, \lVert c \rVert}$$

The part we're proud of is niche-aware retrieval. When a creator pivots, their old off-niche content should stop bleeding into new ideas. So given a stored niche embedding $n$, we keep a chunk $c$ only when its cosine distance to that niche is under a threshold:

$$1 - \text{sim}(c, n) < \tau, \qquad \tau = 0.75$$

Change the niche and the whole corpus re-filters instantly, no re-indexing. For thumbnails we pass the creator's real face and product shots as references and prompt the model to keep identity but re-render it professionally, not paste the source photo.

What we learned

Most of it, honestly. We already knew RAG (we'd built a multi-modal RAG before and tuned it with Optuna), so that was familiar ground. The browser side was all new. We learned how extension auth actually works, since chrome.identity doesn't exist in a content script, which forced OAuth into a background worker with the code exchanged server-side. We learned how to make a React app survive inside someone else's single-page app. And we learned to stop tying state to the frontend.

Challenges we ran into

Day one nearly broke us on a single bug: the extension couldn't call its own backend. Chrome blocks a page on youtube.com from hitting localhost, a Private Network Access rule on top of CORS that we'd never heard of. The fix was routing every request through the background worker.

Studio fought us too. It's an SPA that re-renders constantly, so our injected sidebar kept disappearing until we watched the DOM with a MutationObserver to re-inject it and a ResizeObserver to reposition the panel when the drawer moved.

State kept dying on refresh. Generation was bound to the page, so a reload lost everything mid-run. We moved generation onto backend jobs and made the frontend just mirror server state, so a refresh or a tab switch picks right back up.

What's next

Tighter latency, a trained avatar for sharper identity, and taking it beyond YouTube.

Share this project:

Updates

Submission history