個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗A free music player with full songs, live lyrics, karaoke, story cards and your library on every device.
Spindle is built like an after-hours record store: a living colour field behind everything, a crate of records to dig through, album sleeves you can turn over, and a turntable you can scratch. Every song plays in full, it's free, and there are no ads.
The name is the pin at the centre of a turntable that every record spins on. In the wordmark, the dot of the i is a tiny record that spins while music plays.
Listening
Your library
Sharing
Everywhere
| Layer | What |
|---|---|
| App | React 18, TypeScript, Vite, Framer Motion, Zustand |
| Graphics | WebGL colour field, Canvas story cards, Web Audio visuals and vocal remover |
| Accounts & sync | Supabase (Postgres, Auth, Realtime) with row-level security |
| Server | Vercel functions: importer, share pages, link-preview images |
| Music | YouTube IFrame API, iTunes Search, Audius, LRCLIB lyrics |
src/
api/ iTunes, YouTube lookup, Audius and lyrics clients
components/ player, deck, share sheet, story maker, sign-in gate…
lib/ sync engine, recommendations, autoplay, history & stats, story cards, offline
pages/ Home, Browse, Search, Library, playlists, Stats, Settings, About…
store/ the player and library state (persisted, synced)
server/ importer and share-page logic, shared by dev and Vercel
api/ Vercel functions: /api/import, /api/share (/s/:code), /api/og
supabase/ database schema and branded email templates
public/ brand assets, service worker, privacy and terms pages
src/lib/sync.ts): each library is one JSON document of keyed entries. Devices send only what changed; the database merges entry by entry (newest wins, deletions carry over), and Realtime tells the other devices to pull.src/components/AudioEngine.tsx): finds the YouTube upload for each song, scoring title, artist and length, and falls back to the iTunes preview. Audius tracks play as audio files.src/lib/recommend.ts): the popular songs of the artists a playlist leans on, with name lookups for songs from YouTube or Audius.npm install
npm run dev
Open http://localhost:5173. Without Supabase keys, accounts are switched off and everything is saved on the device, which is handy for working on the UI.
npm test
Vitest covers stats, recommendations, share pages (including escaping), the importer's link checks and the database rules, which run against a real Postgres in memory (PGlite). GitHub Actions runs the type-check, tests and a build on every push.
Create .env.local (never committed):
VITE_SUPABASE_URL=https://<project>.supabase.co
VITE_SUPABASE_ANON_KEY=<publishable key>
VITE_AUTH_PROVIDERS=github # sign-in buttons to show: github, google
VITE_DISCORD_INVITE=<optional> # invite link for the community Discord, shown on Feedback and Settings
VITE_YT_API_KEY=<optional> # YouTube Data API, only used if the free mirrors fail
The Supabase URL and publishable key are public by design; row-level security keeps every library private to its owner.
supabase/schema.sql in the SQL Editor.https://spindle-bice.vercel.app, redirect URLs for production and http://localhost:5173.confirm-signup.html (subject {{ .Token }} is your Spindle code); Reset password uses reset-password.html.https://<project>.supabase.co/auth/v1/callback, then paste its ID and secret under Providers.#/admin shows all feedback and crash reports:
insert into public.admins select id from auth.users where email = '[email protected]' on conflict do nothing;
insert into private.settings values ('discord_webhook', 'https://discord.com/api/webhooks/…')
on conflict (key) do update set value = excluded.value;
The project is deployed on Vercel and every push to main goes live. To deploy your own copy, import the repo into Vercel (free Hobby plan) and add the environment variables above.
desktop/ is a Tauri app: a small native window around the live site, using the browser engine already on the computer (WebView2 on Windows), so the installer is a few MB and it updates whenever the site does. It adds:
spindle:// link), and updates that install from inside the appcd desktop
npm install
npm run dev # run it
npm run build # make an installer in src-tauri/target/release/bundle
It needs Rust. To show Discord status, create an application at discord.com/developers named Spindle and build with its Application ID in SPINDLE_DISCORD_APP_ID. Pushing a tag like desktop-v1.0.0 builds Windows and Mac installers on GitHub and publishes them as a release (set the ID as an Actions variable of the same name).
Updates: releases are signed so installed apps can update themselves. The signing key lives outside the repo; its contents go in the TAURI_SIGNING_PRIVATE_KEY Actions secret, and its public half is in tauri.conf.json. To ship an update, raise version in desktop/src-tauri/tauri.conf.json and Cargo.toml, then push a new desktop-v… tag. The release text becomes the notes in the app's update card. Website changes need no app update: the app loads the live site.
.env.local and Vercel's environment settings.Found something? Please email the address on the privacy page instead of opening a public issue.
| Key | Action |
|---|---|
| Space | Play / pause |
| ← / → | Seek 5 seconds |
| Shift + ← / → | Previous / next song |
| ↑ / ↓ | Volume |
| M | Mute |
| F | Open / close the full player |
| / or Ctrl+K | Search |
Songs stream from YouTube, Apple's iTunes previews and Audius; lyrics come from LRCLIB and lyrics.ovh. Spindle doesn't host or sell any music.
Somesh Sanjay Thakur, Full-Stack & AI Automation Dev. Crafting reactive web apps and autonomous agents with Next.js, React, Tailwind and Python. Shipping at the UX + LLM intersection.
© Somesh Sanjay Thakur. All rights reserved.