Project overview

Before playlists,we made mixtapesone song at a time.

My wife and I were high-school sweethearts. I made her mixtapes to share what I felt. Years later, she showed me she had kept them. So I built a way for us to keep listening together.

A clear cassette case holding a handwritten Love from Me to You tracklist with ten songs on each side
A young couple from the early 1990s walking hand in hand same songs. same us. ♡
The reason it exists

music shares
a love story

While we were dating, the tapes were my attempt to say through music what I did not always know how to put into words.

We married after college. Since then, we have built an amazing family, share a Christian faith that is central to our life together, and watch our relationship continue to grow stronger every day. I think I’m the luckiest man alive. I found the best woman and partner to do life with.

The mixtapes were there near the beginning. Every track had to earn its place. Every side of the tape had a limit. The order meant something. Each mixtape was a small, handmade message about how I felt about her. Obviously the mixtapes don't deserve credit for everything that followed, but they certainly earned a place in our story.

Recently, my wife showed me that she still had them. The handwriting was fading, there was no way to play them, and music had moved to streaming. When she brought the tapes back out, it became an invitation to learn something new while preserving something irreplaceable.

Mixtape Memory project card showing a cassette, a couple holding hands, a heart made from tape, and a Spotify playlist

High-school sweethearts

I chose the songs, wrote every insert, balanced Side A and Side B, and gave each tape to the girl I hoped would hear what I was trying to say.

A life built together

We married after college and built a family, a shared faith, and a partnership that continues to grow stronger with time.

Still playing

When she brought the tapes back out, it became an invitation to learn something new while preserving something irreplaceable.

From paper to playlist

Four steps. Human judgment stays in the loop.

The interface keeps the process understandable: capture the source, review what OCR extracted, connect Spotify, and create the playlist. Automation does the tedious work; the person keeps the final say.

STEP 01

Capture

Take a photo or upload an existing image. The original image is stored in Supabase, while a resized JPEG copy is sent to OCR.

STEP 02

Review

Google Vision reads the handwriting; the parser separates columns and proposes titles and artists.

STEP 03

Connect

Spotify OAuth uses Proof Key for Code Exchange (PKCE) and requests permission to create public or private playlists and read basic Spotify account details, including email.

STEP 04

Create

Matched tracks are added in order and the mixtape, songs, image, and playlist record are preserved.

01 / Start with the object

the original ink is the input, no need to retype

The capture flow accepts a camera photo or upload, then shows the exact image that will be analyzed. That matters because the original insert remains the source of truth.

  • The original image is retained in storage
  • A resized JPEG copy reduces the OCR payload and processing time
  • The source photo stays connected to the saved mixtape
Ready for a camera photo or upload
Empty Scan Your Mixtape capture screen with Take Photo and Upload Image buttons
The photographed insert becomes the source
Capture screen showing the handwritten cassette tracklist ready to use
02 / Read, then verify

OCR is a draft, not a verdict.

Handwriting recognition gets the process moving, but the review screen makes every detected song editable. The correction step is deliberately visible because confidence is not the same thing as correctness.

  • Two-column Side A / Side B detection
  • Noise-reduction, tape-type, duration, and header filtering
  • Flexible separators and 50+ artist abbreviations
  • Original OCR text retained beside the proposed match
Twenty songs detected and ready for review
Review screen showing 20 detected songs from the handwritten insert
A small correction prevents a wrong match downstream
Edit form correcting the first track to She's Got a Way by Billy Joel
03 / Resolve the songs

Fuzzy matching handles imperfect handwriting.

The matcher searches Spotify with the parsed title and artist, then scores the candidates using title similarity, artist similarity, Levenshtein distance, and era. Strong matches move forward; uncertain ones remain reviewable.

That makes a useful failure visible. If “You’re the Inspiration” is incorrectly resolved as “You’re the One That I Want,” the original OCR line remains on the card so a person can catch it before creating the playlist.

Spotify connection uses OAuth with PKCE
Connect to Spotify step in the scan wizard
Album art and metadata make each proposed match inspectable
Create Playlist screen showing album artwork, matches, the playlist name, and songs to be added
04 / Preserve the result

Love from Me to You - Vol 1

The final confirmation closes the loop: 20 handwritten entries become 20 playable songs, while the saved history keeps the insert and playlist connected to the story that created them.

The result is more than a Spotify link. Mixtape Memory saves the photographed insert, the OCR text, each matched song, and the finished playlist as one connected record.

The cassette remains the keepsake. The playlist lets the music travel with us again.

Playlist created with all 20 songs
Playlist Created confirmation for Love from Me to You - Vol 1 with 20 songs
The mixtape remains part of a personal collection
My Mixtapes history showing Love from Me to You - Vol 1 and the insert thumbnail
Under the label

The technology behind the memory

The system joins a typed React interface to managed storage, server-side OCR, a purpose-built parser, Spotify search and playlist APIs, and a relational history protected by row-level security.

Client browser

  • React application
  • TypeScript services and models
  • React Router routes
  • Tailwind CSS interface
  • Lucide React icons
  • Vite build and tree shaking

Supabase platform

  • PostgreSQL records for profiles, mixtapes, songs, playlists, and shared match cache
  • Supabase Auth and session state
  • Storage bucket for source images
  • Deno Edge Function keeps the Google API secret off the client
  • Row-level security isolates each user’s database records

External services

  • Google Cloud Vision document OCR
  • Spotify OAuth authorization with PKCE
  • Spotify catalog search
  • Public or private playlist creation

OCR and text parsing

Vision returns positioned text lines; the client parser turns them into structured tracks.

  • Detects left and right columns using bounding-box centers and line widths
  • Splits wide lines that contain two tracks
  • Recognizes hyphens, dashes, dots, slashes, colons, and “by” separators
  • Filters Dolby, CrO2, EQ, brand, duration, and header noise
  • Outputs side, track number, raw text, cleaned title, artist, and confidence

Intelligent matching

Each parsed song becomes a Spotify search, then candidate results are ranked instead of blindly accepted.

  • Levenshtein-based title and artist similarity
  • Era-aware scoring and preference for original recordings
  • Automatic and manual searches retrieve up to ten candidates
  • The best match and up to three alternatives remain available for review
  • Manual search and override remain available

Data, identity, and history

The application preserves more than a playlist URL; it keeps the source, extraction, match, and result connected.

  • profiles → mixtapes → songs
  • mixtapes → playlists
  • Original OCR text and manually corrected values stored separately
  • Match confidence and override state recorded per song
  • User-owned records protected by PostgreSQL RLS policies

Security and performance

Secrets and large images are handled where they belong, while the browser stays lightweight.

  • The browser uses a Supabase anon key; access to user-owned database rows is governed by RLS
  • Google Vision key is stored as an Edge Function secret
  • Spotify access is scoped to playlist and basic profile permissions
  • Tokens remain client-side and are cleared on disconnect
  • Compression reduces the OCR payload and processing latency
ReactTypeScriptReact RouterTailwind CSSLucide ReactViteSupabase PostgreSQL / Auth / Storage / Edge FunctionsGoogle VisionSpotify Web API