A web app for browsing and playing the MAESTRO v3.0.0 piano MIDI dataset on a real piano. Built for home server deployment — designed to drive a Roland FP digital piano via the Web MIDI API over Bluetooth.
1,276 piano performances from the International Piano-e-Competition, spanning 60 composers and 10 competition years (2004–2018).
Warning
This application is vibecoded. Do not use it as a reference for best practices in Python, FastAPI, SvelteKit, or any other technology. It is a personal project built for fun and learning, not production-quality code. For more information about this please see the about page.
- Browse by composer — portrait cards with images from Wikipedia, sortable track lists
- Browse by competition — year/round/session hierarchy parsed from MIDI filenames
- Client-side MIDI playback — streams MIDI events to any connected output via the Web MIDI API
- Live piano visualization — 88-key display in the player bar lights up with active notes in real time
- Search — full-text search across composers and piece titles
- Transport controls — play, pause, stop, seek, tempo adjustment (0.25x–2.0x), auto-advance queue
- Dark/light theme — dark by default, persisted in localStorage
- All-outputs mode — sends MIDI to every available output simultaneously (no manual device selection needed)
- Single-container Docker deployment — multi-stage build, one image serves everything
| Layer | Technology |
|---|---|
| Backend | Python 3.12+ / FastAPI, managed with uv |
| Frontend | SvelteKit 2 (Svelte 5) + Tailwind CSS 4 |
| Client MIDI | Web MIDI API + @tonejs/midi |
| Composer images | Wikipedia PageImages API, disk-cached |
| Database | None — CSV loaded into memory at startup |
| Deployment | Docker (multi-stage build) |
- Python 3.12+ and uv
- Node.js 22+
- A Chromium-based browser (Chrome, Edge) for Web MIDI API support
Start the backend and frontend dev servers:
# Backend
cd backend
uv sync
uv run uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
# Frontend (in a second terminal)
cd frontend
npm install
npm run devThe frontend dev server proxies API requests to localhost:8000. Open http://localhost:5173 in your browser.
docker compose up --buildThis builds a single image that serves both the API and the compiled frontend on port 8000. If no dataset is found at startup, the backend downloads and extracts MAESTRO v3.0.0 automatically.
The image is built to comply with OpenShift's restricted / restricted-v2 SCC:
- All files are owned by
UID 1000 / GID 0(root group) withchmod g=uso any arbitrary UID injected by the namespace can read and write through group 0. - The
USERdirective uses the numeric UID1000— named users are not resolvable when OpenShift assigns a high UID at runtime. - Port 8000 is unprivileged, no special port capabilities are required.
allowPrivilegeEscalation: falseandcapabilities.drop: [ALL]are set in the container securityContext.
# Create the dataset PVC first
oc apply -f openshift/pvc.yaml
# Deploy the application (edit image: field first)
oc apply -f openshift/deployment.yaml
oc apply -f openshift/service.yaml
oc apply -f openshift/route.yamlThe openshift/ directory also contains a route.yaml with TLS edge termination. Set the host: field or remove it to let OpenShift auto-generate a hostname.
Dataset auto-download: the first startup downloads MAESTRO v3.0.0 (~1.4 GB) into the PVC. The PVC request is 3 Gi to leave headroom. If your cluster lacks egress access to Google Cloud Storage, pre-populate the PVC and set
MAESTRO_AUTO_DOWNLOAD_DATASET=false.
# Build the frontend
cd frontend
npm ci && npm run build
# Run the backend (serves the built frontend automatically)
cd ../backend
uv sync
uv run uvicorn app.main:app --host 0.0.0.0 --port 8000All settings use the MAESTRO_ prefix and can be set as environment variables:
| Variable | Default | Description |
|---|---|---|
MAESTRO_DATASET_PATH |
../MaestroDataset |
Path to the MAESTRO dataset directory |
MAESTRO_AUTO_DOWNLOAD_DATASET |
true |
Download and extract dataset automatically when CSV is missing |
MAESTRO_DATASET_DOWNLOAD_URL |
https://storage.googleapis.com/magentadata/datasets/maestro/v3.0.0/maestro-v3.0.0-midi.zip |
Dataset ZIP URL used for auto-download |
MAESTRO_DATASET_DOWNLOAD_TIMEOUT_SECONDS |
1800 |
Timeout for dataset ZIP download |
MAESTRO_IMAGE_CACHE_DIR |
.cache/composer_images |
Cache directory for composer portraits |
MAESTRO_HOST |
0.0.0.0 |
Server bind address |
MAESTRO_PORT |
8000 |
Server port |
MAESTRO_ENV |
development |
Set to production to disable the API docs UI |
MAESTRO_CORS_ORIGINS |
["http://localhost:5173","http://localhost:8000"] |
JSON list of allowed origins |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/health |
Health check with track/composer/year counts |
| GET | /api/tracks |
List tracks (query: ?q=, ?composer=, ?year=, ?sort=, ?order=) |
| GET | /api/tracks/{id} |
Single track by ID |
| GET | /api/composers |
List composers with piece counts and durations |
| GET | /api/composers/{slug}/image |
Composer portrait (Wikipedia or SVG placeholder) |
| GET | /api/composers/{slug}/tracks |
Tracks by composer |
| GET | /api/competitions |
Competition years with round/session summaries |
| GET | /api/competitions/{year} |
Full year data with rounds, sessions, tracks |
| GET | /api/midi/tracks/{id}/file |
Raw MIDI file download |
| GET | /api/midi/ports |
Available server-side MIDI output ports |
| WS | /api/playback |
Server-side playback control (play, pause, stop, seek, tempo) |
MaestroMIDIPlayer/
├── MaestroDataset/ # Bind-mount point for MAESTRO v3.0.0 dataset
├── backend/
│ ├── pyproject.toml
│ ├── app/
│ │ ├── main.py # FastAPI app, middleware, static file serving
│ │ ├── core/
│ │ │ ├── config.py # Settings via pydantic-settings
│ │ │ └── limiter.py # Rate limiter instance
│ │ ├── data/
│ │ │ ├── models.py # Pydantic models
│ │ │ ├── dataset.py # CSV loader, in-memory store, search
│ │ │ └── filename_parser.py # Extract round/session from filenames
│ │ ├── services/
│ │ │ └── composer_images.py # Wikipedia image fetcher + cache
│ │ └── routers/ # API route handlers
│ └── tests/
├── frontend/
│ ├── package.json
│ ├── src/
│ │ ├── lib/
│ │ │ ├── api.ts # Backend API client
│ │ │ ├── midi-player.ts # Web MIDI playback engine
│ │ │ ├── stores.ts # Svelte stores + TypeScript interfaces
│ │ │ └── utils.ts # Duration formatting, etc.
│ │ ├── components/ # PlayerBar, SearchBar, ComposerCard, etc.
│ │ └── routes/ # SvelteKit pages
│ └── static/
├── Dockerfile # Multi-stage build
└── docker-compose.yml
The MAESTRO dataset is not included in this repository. If maestro-v3.0.0.csv is missing in MAESTRO_DATASET_PATH, the backend auto-downloads and extracts MAESTRO v3.0.0 at startup.
The dataset is provided under CC BY-NC-SA 4.0.
This project is for personal/educational use. The MAESTRO dataset has its own license (CC BY-NC-SA 4.0).