Content
  1. Overview
  2. Context
  3. The Experience
  4. Visual System
  5. Technical
  6. Learnings

A real-time fortune teller from your Spotify recents

Every day I listen to music on repeat, but I rarely leave a trace that isn't a queue or a year-end recap. bumps turns that habit into a public diary — three recent tracks, one cryptic fortune.

2026/Design Engineering/bumps-2025.vercel.app

WhenOct '25 –– May '26
ForPersonal
DisciplineDesign engineering, front-end, interaction, WebGL
ToolsNext.js, React, Three.js, Spotify Web API, OpenAI, Web Audio API

Overview

bumps is a personal web diary scored by my three most recent Spotify plays. Connect your account, and the app pulls recently played tracks, fetches lyrics when available, and asks GPT-4o-mini for a single fortune-cookie line — cryptic, second-person, never naming the songs. That line tumbles through a Three.js viewport like a physical slip of paper while the page background bleeds color from whichever track is active.

Built over seven months, from a static index.html prototype to a Next.js app with a server-side OpenAI proxy — part fortune teller, part self-portrait, entirely scored by whatever I played last.

Context

Daily music listening is abundant but rarely reflected in a form that isn't a screenshot or a Wrapped summary. bumps asks: what if the day's emotional residue could be compressed into one line — grounded in what you actually played, not what an algorithm thinks you like?

The product frames everyday listening as something worth publishing — not a playlist widget, but a diary. Three song cards, a scrolling "now playing" capsule, and a fortune that lands like a physical slip of paper. Intimate (your Spotify account, your recent plays) yet legible to anyone scrolling past.

A public diary scored by my top 3 tracks of the day.

The Experience

After Spotify OAuth (PKCE), the app deduplicates recently played tracks to the three most recent unique songs, fetches lyrics from lyrics.ovh, and generates a fortune with intentional delay — a random 3–5s minimum so generation feels like reading, not loading.

— Song stack

Three glassmorphic cards sit in a stack; the active track stays bright, the others dim. Click a dimmed card to promote it — the marquee updates, optional 30s preview audio plays, and the background re-derives from that cover art.

— Fortune reveal

The generated line renders on procedurally textured paper — canvas-drawn grain, warm gradients, edge shading, Libre Baskerville italic — then animates across the full viewport in an 11.5s eased fall with dual-axis sway and shadow sync. A synthesized swoosh marks the reveal; the line persists under the song stack after the paper is disposed.

— Refresh ritual

FAB buttons handle Spotify connect/disconnect and manual refresh. One fortune per cycle; session history deduplicates repeats across up to three generation attempts.

Ritual over dashboard

The stakes are small and human: this isn't a product pitch. It's a daily habit turned into something shareable — listening as performance, meaning exported as a one-liner diary entry.

InsightInteraction design as pacing — forced wait before reveal, 11.5s fall choreography, and card-driven environmental color, not clicks-per-minute.

Visual System

The background is not decorative wallpaper. It samples the active track's cover art into four boosted colors, cross-fades between gradient layers over 700ms, and layers SVG grain for a printed, nostalgic surface.

Typography splits roles: IBM DOS for the interface shell, Inter for the marquee and quote, Libre Baskerville (italic) on the fortune slip itself. Procedural kick-drum clicks on buttons; swoosh on reveal — sensory feedback without asset files.

  1. 1Cover-art gradient extraction — 80×80 sample, saturation/luminance clustering, four-stop radial CSS
  2. 2Fortune paper texture — 2× DPR canvas, noise tile, line wrap capped at two lines
  3. 3Reduced motion — CSS disables animations when prefers-reduced-motion: reduce

Technical

A ~2.2k-line client engine (lib/bumps-app.js) wrapped in a thin Next.js 15 + React 19 shell. The React page mounts static markup; all behavior lives in the client module migrated from the original prototype without rewriting logic.

  1. 1Spotify Authorization Code + PKCE — scopes for email, private profile, recently-played; tokens in localStorage with refresh on 401
  2. 2POST /api/chat — proxies OpenAI gpt-4o-mini so the API key stays off the client
  3. 3lyrics.ovh — free lyrics lookup with graceful degradation to title/artist-only prompts
  4. 4Three.js fortune scene — single mesh typical; renderer capped at 2× device pixel ratio
  5. 5Gradient cache — localStorage persists cover-derived colors across sessions

Fortune prompt constraints: under 12 words, second person, cryptic and poetic, draws subtle inspiration from song themes, never names artists or tracks. Temperature set to 0.95 for variety; session dedup reduces repetition.

Learnings

bumps is a case study in progressive hardening: PKCE client auth, CORS-safe OpenAI proxy, resilient JSON parsing with fallbacks, token refresh on 401, and a single large module migrated into Next without rewriting behavior.

The honest evolution: static prototype → deployable app. Cover-driven color replaced a manual gradient customizer. The fortune paper disposes after its fall — scattered draggable fortunes were explored but cut to keep the ritual focused on one line at a time.

Seven months from first commit to Next.js migration — editorial software where type hierarchy, grain overlays, and cover-driven color automate the mood board.

bumps-2025 on GitHub