An interactive Valentine's Day web experience — a cosmic garden of memories, music, and little surprises, built as a personal gift.
Live repo: github.com/JoaoMarcos44/cosmic-bloom
Cosmic Bloom is a single-page romantic experience. When someone opens the site, they first see a Valentine welcome screen with a personal message. After entering, they arrive at a living garden where they can plant flowers, read whispers, play curated songs, unlock colors through a couple quiz, spin a date gacha, seal a time capsule, and watch animated visual memories of the couple.
Everything runs in the browser — no backend server required. Personal data (playlist choices, time capsule, welcome state) is stored locally via localStorage and sessionStorage.
- Animated welcome screen shown once per browser session
- Custom message in English for the recipient
- OAuth return (
?code=/?error=) skips the welcome and goes straight to the garden
- Plant flowers — tap the dock to grow flowers on an interactive canvas
- Whispers — floating love quotes picked from a local curated list (no external API)
- Sakura petals — blossom animation triggered by interactions
- Mood indicator — garden mood reacts to how many flowers have been planted
- Hero couple — ambient scene with local video assets
- Modal with couple videos and background music
- Media served from
public/media/(self-hosted, no external CDN dependency)
- Embedded “Now playing” player with a curated soundtrack
- OAuth PKCE flow for optional Spotify account connection
- Default playlist includes songs like Iris, Photograph, Telomeres, You and Me, and more
- Playlist and active track persist in
localStorage
- Date Gacha — random date idea generator
- Couple Quiz — unlock new flower colors by answering questions
- Time Capsule — write a message sealed until a chosen future date (stored in
localStorage)
| Layer | Technology |
|---|---|
| Runtime / package manager | Bun |
| Framework | React 19 + TanStack Start / Router |
| Build tool | Vite 7 |
| Styling | Tailwind CSS 4 |
| Animation | Framer Motion |
| UI primitives | Radix UI |
| Deploy target | GitHub Pages (SPA) + Cloudflare (optional SSR) |
src/
├── components/
│ ├── garden/ # Main garden UI, modals, Spotify integration
│ └── welcome/ # Valentine welcome screen
├── config/
│ ├── default-tracks.ts # Curated Spotify playlist
│ └── storage-keys.ts # Centralized localStorage keys
├── hooks/
│ └── useWelcomeGate.ts # Welcome screen session logic
├── lib/
│ ├── browser-storage.ts # Safe JSON read/write for storage APIs
│ └── gardenAssets.ts # Local media paths
└── routes/
└── index.tsx # Home: welcome gate → garden
Design patterns used:
- Custom hooks —
useGardenState,useGardenModals,useWelcomeGate,usePlaylists - Single responsibility — storage, auth, and UI separated into focused modules
- Configuration over magic strings — keys and defaults live in
src/config/
- Bun installed
bun install
bun run devThe dev server binds to
127.0.0.1:8888on purpose — Spotify OAuth does not acceptlocalhostas a redirect URI.
bun run build
bun run previewURL: https://joaomarcos44.github.io/cosmic-bloom/
Every push to main triggers .github/workflows/deploy.yml, which:
- Builds the app in SPA mode (
GITHUB_PAGES=true) - Copies
index.html→404.htmlfor client-side routing - Publishes
dist/clientto GitHub Pages
- Open Settings → Pages
- Under Build and deployment, set Source to GitHub Actions
- (Optional, for Spotify login in production) Add repository secret:
- Name:
VITE_SPOTIFY_CLIENT_ID - Value: your Spotify Client ID from the Dashboard
- Name:
- In the Spotify Dashboard, add this Redirect URI:
https://joaomarcos44.github.io/cosmic-bloom
The embed player works without the secret; Spotify account connection requires step 3.
The embed player works without login. Connecting a Spotify account is optional and uses the Web API with PKCE (no client secret on the frontend).
- Create an app at Spotify Developer Dashboard
- Set Redirect URI to exactly:
- Local dev:
http://127.0.0.1:8888 - Production:
https://joaomarcos44.github.io/cosmic-bloom
- Local dev:
- Copy
.env.exampleto.envand fill in your Client ID:
VITE_SPOTIFY_CLIENT_ID=your_client_id_here
VITE_SPOTIFY_REDIRECT_URI=http://127.0.0.1:8888- Restart the dev server after changing
.env
Never commit .env — it is listed in .gitignore. Only .env.example (with empty values) belongs in the repo.
Local files in public/media/:
| File | Usage |
|---|---|
couple-hug.mp4 |
Visual memory modal |
couple-sing.mp4 |
Hero scene / visual memory |
rising-on-the-mountain.mp3 |
Background music in visual memory |
- Fixed broken Lovable export by self-hosting video/audio assets
- Full English UI for an international recipient
- Valentine welcome page with session-based gate
- Spotify OAuth PKCE + embedded player with curated default playlist
- Local whisper quotes (replaced unreliable external API)
- Time Capsule with
localStoragepersistence - Clean-code refactor: centralized storage, typed modules,
LivingGardenshell, clear English naming - Dev server configured for Spotify redirect compatibility (
127.0.0.1:8888)
.envand.dev.varsare gitignored- No client secrets in the codebase — Spotify PKCE is designed for public clients
- OAuth tokens are stored only in the user's browser (
localStorage) - No backend means no server-side database or user accounts
Private personal project. All rights reserved.