SoundCluster turns searched songs into Gemini-generated 5D emotion vectors and renders those vectors as a 3D music constellation with React Three Fiber.
soundcluster-jhu/
|-- client/ # React + Vite frontend
| `-- src/
| |-- canvas/ # R3F/WebGL scene, star nodes, relation lines
| |-- components/ # 2D HUD, search, result panel, controls, share modal
| |-- constants/ # frontend-only UI constants
| |-- context/ # analysis stream context
| |-- hooks/ # EventSource and stream state hooks
| |-- utils/ # projection, snapshot, API client helpers
| |-- data/ # initial mock tracks
| `-- types/ # frontend-specific aliases
|
|-- server/ # Express backend
| `-- src/
| |-- app.ts # Express app, CORS, route mounting, health route
| |-- config/ # env, MongoDB, Gemini, iTunes, LRCLIB clients
| |-- controllers/ # request orchestration, SSE response handling
| |-- routes/ # route bindings
| |-- services/ # analysis pipeline and Gemini integration
| |-- repositories/ # MongoDB persistence for cache and snapshots
| `-- validation/ # LLM response validation
|
|-- shared/ # contracts shared by FE and BE
| |-- constants/ # route prefixes, model defaults, server constants
| |-- types/ # API payload and domain types
| `-- utils/ # pure validation and mapping helpers
|
|-- tests/ # node:test contract tests
|-- docs/ # project planning and operating documents
`-- codex/skills/ # project-specific Codex skills
App.tsxis the main state coordinator.- Search input calls the backend iTunes proxy.
- Result rows trigger lyrics lookup and Gemini analysis streaming.
- The render source of truth is
ClusterShareSnapshot. - Track emotion values are not user-editable; users only toggle which axes are used for projection.
StarsCanvas.tsxprojects 5D emotion vectors into 3D coordinates and renders:- background starfield
- dotted 3D axis reference lines
- track nodes
- selected track state
- nearest and farthest relation lines
- hover and pinned metadata popups
- Express exposes API routes under
/api/*. - CORS is configured for local and deployed frontend access.
- iTunes and LRCLIB are called only from the backend.
- Gemini is called only from the backend.
- MongoDB Atlas stores:
- analysis cache keyed by normalized title and artist
- share snapshots keyed by
nanoid - snapshot hashes to reuse existing share ids for identical data
- Shared route constants prevent frontend/backend path drift.
- Shared types define iTunes metadata, lyrics results, SSE events, music analysis results, and share snapshots.
- Shared validation verifies share snapshot shape before server persistence.
Track
|-- id
|-- itunesTrackId?
|-- itunesUrl?
|-- title
|-- artist
|-- albumImageUrl?
`-- emotions
|-- energy
|-- valence
|-- tempoDensity
|-- spaceDepth
`-- tension
ClusterShareSnapshot
|-- version
|-- selectedTrackId
|-- cameraPosition
|-- cameraTarget
`-- tracks[]
analysis_cache
|-- cacheKey
|-- title
|-- artist
|-- analysis
|-- emotions
|-- createdAt
`-- updatedAt
share_snapshots
|-- shareId
|-- snapshotHash
|-- snapshot
|-- createdAt
`-- updatedAt
MongoDB indexes are created from server/src/config/db.ts.
- R3F or Three.js rendering code goes under
client/src/canvas/. - Ordinary React HUD components go under
client/src/components/. - Frontend API clients and pure projection helpers go under
client/src/utils/. - Backend route bindings go under
server/src/routes/. - Request orchestration goes under
server/src/controllers/. - Gemini/cache/business flow goes under
server/src/services/. - MongoDB persistence goes under
server/src/repositories/. - Cross-layer constants, payload types, and pure validators go under
shared/.
- The app uses iTunes Search API, not Spotify.
- Lyrics are optional. LRCLIB failure does not block Gemini analysis.
- Gemini response values must remain in the
0.0to1.0range. - Share URLs store only the compact snapshot id in the URL; the snapshot body is stored in MongoDB Atlas.
- Camera POV is restored from snapshot defaults or stored snapshot values, while projection is recomputed on the frontend.