Spoticulum is an open-source Spotify collection-image maker. Connect a Spotify account, choose top artists or top tracks, and download a shareable image assembled in the browser.
The app is built with React 19, Vite, TypeScript, and Express 5. Spotify OAuth tokens stay on the server in an HTTP-only session; the client talks to same-origin /api/* routes and never receives Spotify access or refresh tokens.
- Node
24.22.1from.nvmrc - npm
11.x - A Spotify Developer application
nvm install
nvm use
npm --prefix client ci
npm --prefix server ci
cp server/.env.example server/.envCreate a Spotify app in the Spotify Developer Dashboard and add this redirect URI:
http://127.0.0.1:8888/api/logged
The callback must be exactly that URL, including 127.0.0.1.
server/.env.example already sets the local URLs. Replace the Spotify
credential placeholders. Every variable is listed in the table below.
REDIRECTURI=http://127.0.0.1:8888/api/logged
CLIENT_REDIRECTURI=http://127.0.0.1:3000/
Start the app:
npm run devOpen http://127.0.0.1:3000.
The API runs on http://127.0.0.1:8888, and Vite proxies /api to it during
development.
Server runtime variables:
| Name | Required | Description |
|---|---|---|
CLIENT_ID |
Yes | Spotify application client ID. |
CLIENT_SECRET |
Yes | Spotify application client secret. Keep this server-side only. |
SESSION_SECRET |
Yes | Long random value used to sign session cookies. |
REDIRECTURI |
Yes | Spotify OAuth callback URL, for example http://127.0.0.1:8888/api/logged. |
CLIENT_REDIRECTURI |
Yes | App URL to return users to after OAuth. |
PORT |
No | API port. Defaults to 8888. |
COOKIE_SECURE |
No | Set to true to force secure cookies outside production. |
LOG_LEVEL |
No | Minimum server log level: debug, info (default), warn, error, or silent. |
Client build variable:
| Name | Required | Description |
|---|---|---|
VITE_GA |
No | Optional Google Analytics measurement ID. Analytics remains disabled until the user consents. |
Never put CLIENT_SECRET, SESSION_SECRET, Spotify access tokens, or Spotify
refresh tokens in client environment variables.
Run these from the repository root:
npm run dev
npm run typecheck
npm run lint
npm test
npm run build
npm run verifynpm run verify runs typecheck, lint, tests, format checks, and the production
build. Vitest covers the client, and the Node test runner covers the API.
Install Playwright's Chromium browser before the first end-to-end run:
cd client
npx playwright install chromium
npm run test:e2eA local end-to-end run compiles the server before the tests start. In CI, the
workflow builds the server first and Playwright only starts server/dist/app.js.
The tests use mocked Spotify responses, so they do not require a real Spotify
account.
CI fails on high-severity dependency findings:
npm --prefix client audit --audit-level=high
npm --prefix server audit --audit-level=highBuild and run locally:
npm --prefix client ci
npm --prefix server ci
npm --prefix server run build
cd server
NODE_ENV=production npm startIn production:
- Serve the app and API from the same HTTPS origin.
- Set
REDIRECTURIandCLIENT_REDIRECTURIto that same origin. - Add the production
REDIRECTURIto the Spotify Developer Dashboard. - Keep
CLIENT_SECRETandSESSION_SECRETas runtime secrets. - Use one replica unless you add a shared session store; in-memory sessions are cleared on restart and are not shared across containers.
The included Dockerfile builds the client and server into a single production image:
docker build -t spoticulum .
docker run --env-file server/.env -p 8888:8888 spoticulumThe server writes one JSON event per line to stdout. Use the terminal running
npm run dev for local logs, or docker logs <container> for a container.
API completion events include a request ID, route, status, and duration; the
same ID is returned in the X-Request-Id response header. Successful health
checks and static assets are omitted. Logs do not include request URLs, OAuth
codes or state, cookies, Spotify tokens, or Spotify response bodies.
Build-time analytics can be supplied with:
docker build --build-arg VITE_GA=G-XXXXXXXXXX -t spoticulum .- Spotify tokens stay in the HTTP-only server session.
- CORS only allows the configured app origin and local development origins.
- The generated image is assembled in the browser and is not uploaded by the app.
Spoticulum is released under the MIT License.
