

Deciding what to watch was harder than watching it
Deciding what to watch was harder than watching it
I love movies and shows.
I love movies and shows.
But finding something to watch often meant jumping between platforms, scrolling for too long, and still not finding anything.
But finding something to watch often meant jumping between platforms, scrolling for too long, and still not finding anything.
That frustration made me wonder:
That frustration made me wonder:
"What if discovering titles felt less like searching and more like exploring?"
"What if discovering titles felt less like searching and more like exploring?"
So I built Atlas.
So I built Atlas.
I started by removing the feed
I started by removing the feed
I didn't want to build another page of recommendations.
I didn't want to build another page of recommendations.
I wanted movies to exist in a space you could explore.
I wanted movies to exist in a space you could explore.
Atlas is built around an infinite canvas inspired by Figma and Google Maps.
Atlas is built around an infinite canvas inspired by Figma and Google Maps.
Drag to explore.
Drag to explore.
Search when you know what you want.
Search when you know what you want.
Filter when you need more control.
Filter when you need more control.
My first infinite canvas wasn't actually infinite
My first infinite canvas wasn't actually infinite
My first version duplicated the grid to create the illusion of endless space.
My first version duplicated the grid to create the illusion of endless space.
It worked until posters started repeating, which did not feel like an infinite canvas.
It worked until posters started repeating, which did not feel like an infinite canvas.
So I replaced it with a spiral coordinate system where every movie gets a unique position radiating outward from the center.
So I replaced it with a spiral coordinate system where every movie gets a unique position radiating outward from the center.
This gave Atlas a world that could keep expanding without visible edges.
This gave Atlas a world that could keep expanding without visible edges.
1const ring = Math.ceil((Math.sqrt(n + 1) - 1) / 2)2const position = n - (2 * ring - 1) ** 23 4const side = Math.floor(position / (2 * ring))5const offset = position % (2 * ring)6 7// Every movie gets a unique position8// radiating outward from the center.9return getCoordinate(ring, side, offset)Exploration still needed control
Exploration still needed control
Exploring works when you don't know what you want.
Exploring works when you don't know what you want.
Sometimes you do.
Sometimes you do.
So I added Search for movies, shows, and filmographies, alongside Filters for genre, language, year and rating.
So I added Search for movies, shows, and filmographies, alongside Filters for genre, language, year and rating.
1const [query, setQuery] = useState('')2const [filters, setFilters] = useState({3 genre: null,4 language: null,5 year: null,6 rating: null,7})8 9const results = applySearchAndFilters(movies, query, filters)Small details made Atlas feel alive
Small details made Atlas feel alive
Selecting a poster opens the Inspector, bringing together cast, trailers, streaming options, and ratings from multiple sources.
Selecting a poster opens the Inspector, bringing together cast, trailers, streaming options, and ratings from multiple sources.
But I didn't want it to feel like opening a database.
But I didn't want it to feel like opening a database.
Atlas samples each movie's artwork and uses its color as ambient lighting.
Atlas samples each movie's artwork and uses its color as ambient lighting.
Every movie subtly changes the atmosphere of the interface.
Every movie subtly changes the atmosphere of the interface.
1// Sample color from the movie artwork2const pixels = ctx.getImageData(3 centerX - 20,4 centerY - 20,5 40,6 407).data8 9const ambientColor = averagePixels(pixels)10 11setInspectorColor(ambientColor)I wanted interactions to feel physical
I wanted interactions to feel physical
Saving a movie originally just updated the watchlist.
It worked, but it didn't feel like Atlas.
So I made the poster travel along a curved path toward the watchlist.
This helped the product feel tactile instead of flat.
1const x = bezier(startX, midX, targetX, t)2const y = bezier(startY, midY, targetY, t)3 4gsap.set(thumbnail, {5 x,6 y,7 scale: 1 - t * 0.7,8 opacity: 1 - t * 0.459})Then more movies made everything slower
Then more movies made everything slower
With posters being rendered almost immediately, panning started dropping frames.
With posters being rendered almost immediately, panning started dropping frames.
For Atlas, that wasn't just an engineering problem.
For Atlas, that wasn't just an engineering problem.
Movement was the experience.
Movement was the experience.
I rebuilt the canvas to render only what's visible and preload content in the direction people are moving.
I rebuilt the canvas to render only what's visible and preload content in the direction people are moving.
That keeps the experience responsive even with a large number of posters on the backend.
That keeps the experience responsive even with a large number of posters on the backend.
1const visibleCells = getVisibleCells({2 panX,3 panY,4 viewport,5 buffer6})7 8render(visibleCells)9 10// Load posters before they enter the viewport11prefetch(getCellsInDirection(dragVelocity))Then mobile changed the rules
Atlas started desktop first, but mobile forced me to rethink the interaction model.
Atlas started desktop first, but mobile forced me to rethink the interaction model.
Hover does not exist on touch. Poster sizes had to stay usable. Gestures needed to feel natural.
Hover does not exist on touch. Poster sizes had to stay usable. Gestures needed to feel natural.
So I designed Atlas to work across mouse, trackpad, and touch while keeping the same spatial logic underneath.
So I designed Atlas to work across mouse, trackpad, and touch while keeping the same spatial logic underneath.
I wanted insight without surveillance
I wanted insight without surveillance
Once Atlas was live, I needed to understand how people used it.
Once Atlas was live, I needed to understand how people used it.
Were they exploring? Searching? Saving? Returning?
Were they exploring? Searching? Saving? Returning?
So I added privacy first analytics with no cookies, no persistent identifiers, no autocapture, and no full IP storage.
So I added privacy first analytics with no cookies, no persistent identifiers, no autocapture, and no full IP storage.
This gives me enough signal to improve Atlas without collecting data I don't need.
This gives me enough signal to improve Atlas without collecting data I don't need.
1posthog.init(key, {2 persistence: "memory",3 autocapture: false,4 disable_session_recording: true,5 ip: false,6})What Atlas taught me
What Atlas taught me
Atlas started as a personal frustration and became a product about a different kind of discovery.
Atlas started as a personal frustration and became a product about a different kind of discovery.
The value is not just the movie data.
The value is not just the movie data.
It is the feeling of moving through it.
It is the feeling of moving through it.
The canvas, shortcuts, search, filters, inspector, watchlist animation, touch support, and performance work all exist to protect that feeling.
The canvas, shortcuts, search, filters, inspector, watchlist animation, touch support, and performance work all exist to protect that feeling.
The interface wasn't supporting the product.
The interface wasn't supporting the product.
The interface was the product.
The interface was the product.
What's next
What's next
Atlas is live, but I still want to keep improving it.
Atlas is live, but I still want to keep improving it.
The next step is to test whether spatial discovery actually helps people find better things to watch, then use that feedback to refine the canvas, the shortcuts, and the recommendation flow.
The next step is to test whether spatial discovery actually helps people find better things to watch, then use that feedback to refine the canvas, the shortcuts, and the recommendation flow.
Atlas
Movie and Show Discovery
Turning movie and TV show browsing into exploration
Turning movie and TV show browsing into exploration
Designed and built an infinite canvas experience for discovering movies and shows beyond the traditional feed.
Designed and built an infinite canvas experience for discovering movies and shows beyond the traditional feed.
Role
Role
Design Engineer
Design Engineer
Team
Team
Solo project
Solo project
Solo project
Tech Stack
Tech Stack
Next.js 14 • TypeScript • Tailwind CSS • Framer Motion • GSAP • Vercel • PostHog
Next.js 14 • TypeScript • Tailwind CSS • Framer Motion • GSAP • Vercel • PostHog
Next.js 14 • TypeScript • Tailwind CSS • Framer Motion • GSAP • Vercel • PostHog
AI Tools
Cursor • Claude Code • Kiro
AI Tools
Cursor • Claude Code • Kiro
Cursor • Claude Code • Kiro
Status
Status
Live
Live
Try Here
Explore Atlas