A personal osu! beatmap recommendation website. Sign in with your osu! account and get a Spotify-like feed of ranked maps tuned to your top plays — with cover art, 30-second audio previews, real pp values, and like/dislike to refine your taste.
Built for the "I don't know what to play" moment.
First visit — a short taste quiz:
- Sign in with osu! (OAuth).
- Pick your genres (at least 2) — Anime, Electronic, Rock, Metal, …
- Rate 10 songs from those genres in the player. This is what teaches the system your music taste.
After that — the player: one map at a time, full-screen, with its audio preview playing. Rating is mandatory is how you move to the next track, and every rating rebuilds your profile, so recommendations shift as you go.
What drives a recommendation:
- Genre — the strongest signal, from your quiz answers and every rating since.
- Difficulty — from your osu! top plays, so maps stay at your skill level (or override it with the difficulty filter).
- BPM / mappers — learned from what you like.
- Maps you've played or already seen are skipped; pp is computed locally.
- Go to https://osu.ppy.sh/home/account/edit → OAuth → New OAuth Application.
- Name it
spotiosu. - Application Callback URL — set it exactly to:
http://localhost:8000/auth/callback - Copy the Client ID and Client secret into
config.json:{ "osu_api": { "client_id": 12345, "client_secret": "SECRET" } }
Taste profiles live in PostgreSQL, run via Docker:
docker compose up -dThe schema is created automatically on first start, and a legacy data.json
(if present) is imported once and renamed to data.json.migrated.
.\run_web.ps1First run creates a virtualenv and installs dependencies. Then open http://localhost:8000 and click Sign in with osu!.
The DSN comes from
config.json(database.dsn) and can be overridden with theSPOTIOSU_DSNenvironment variable.
- Keyboard:
←dislike ·→like ·spaceplay/pause. - Difficulty filter — "Match my skill" follows your top plays; uncheck it and drag the sliders for an exact ★ range (respected strictly).
- Mods (e.g.
HDDT) — targets maps that play well with them; pp is computed with them. - Preview volume starts at 20% every time, adjustable with the slider.
- Autoplay previews — checkbox under the player, off by default; your choice is remembered in the browser.
- Open in osu! opens the beatmap page. Reset history lets older maps return.
- Retake quiz clears your genres/ratings and starts the questionnaire over.
Anyone can sign in with their own osu! account and gets their own personalised player.
webapp/
server.py FastAPI app: OAuth login, session, JSON API
oauth.py osu! "Sign in with osu!" (Authorization Code flow)
static/ index.html, style.css, app.js (the Spotify-like UI)
bot/ the recommendation engine (reused by the web app)
osu_api.py osu! API v2 client (data + .osu download)
recommender.py taste profile, genre weighting, candidate search, scoring
genres.py osu! genre ids used by the quiz and search
pp.py mod parsing + rosu-pp pp calculation
db.py PostgreSQL pool + schema
store.py per-user state: genres, ratings, seen maps, learned weights
config.py config.json loading
docker-compose.yml PostgreSQL 17
config.json your credentials (gitignored)
The same engine also runs as a terminal tool and a legacy osu! chat bot:
.\run.ps1 --console— type!rin the terminal for your own recommendations..\run.ps1— Tillerino-style IRC chat bot (note: unreliable with osu!lazer, which is why the website is the primary experience). Needsosu_irccreds in config.
rosu-pp-pyis optional; without it recommendations still work but omit pp.- The site is read-only toward osu! (public API + your identity). It never plays for you.
- Your session is a signed local cookie; the secret lives in
.session_secret(gitignored).