No description
  • JavaScript 62.5%
  • Go 19.3%
  • Vue 17.3%
  • CSS 0.6%
  • Makefile 0.2%
Find a file
Julien COSMAO 2a8a7477dc
All checks were successful
ci / web (push) Successful in 1m36s
ci / osv (push) Successful in 45s
ci / trivy-fs (push) Successful in 1m11s
ci / go (push) Successful in 4m11s
ci / hadolint (push) Successful in 39s
ci / app (push) Successful in 3m25s
ci / deploy (push) Successful in 8s
fix(remote): one tap is enough, controllers survive stream drops
- The target ran a command only from a device in its controller list, so
  a command beating the hello (after a reconnect) was dropped. A command
  now attaches its sender: the hub only relays within one user anyway.
- Controllers are no longer dropped when they blink out of the device
  list; only on bye, or after 30 s of failed sends.
- State changes are published from a watcher instead of a timer: a
  background tab throttles timers (1 s, up to 1 min), which left the
  phone showing a stale play button.
- The phone flips play/pause right away; the next snapshot corrects it.
- In remote mode the local engine no longer builds the synth on the
  first touch or when the track list opens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:32:37 +02:00
.forgejo/workflows ci(web): drop node_modules before teardown to stop leaking job networks 2026-10-03 22:13:08 +02:00
assets docs: simplify README and refresh the screenshot 2026-09-22 00:34:48 +02:00
cmd feat(remote): control a playing session from another device 2026-10-11 22:37:46 +02:00
internal fix(remote): survive reloads and dead streams 2026-10-11 23:36:44 +02:00
testdata fix(chords): no keys/vocal stand-in over the file's own chord diagram 2026-09-11 12:09:35 +02:00
web fix(remote): one tap is enough, controllers survive stream drops 2026-10-12 21:32:37 +02:00
.dockerignore Dockerfile 2026-06-10 20:01:20 +02:00
.gitignore feat(tab): redesign rhythm notation and move legends into collapsible panels 2026-09-19 21:54:27 +02:00
.golangci.yml fix(lint): clear golangci-lint findings + add config 2026-06-27 19:36:31 +02:00
.hadolint.yaml chore(docker): retag base image to 4.7.3 to match contents 2026-06-27 19:18:27 +02:00
CLAUDE.md add CLAUDE.md 2026-08-08 23:23:39 +02:00
Dockerfile fix(docker): declare the unprivileged user in the base image 2026-08-09 01:17:23 +02:00
Dockerfile.base fix(docker): declare the unprivileged user in the base image 2026-08-09 01:17:23 +02:00
go.mod build(deps): go 1.26.9 and x/net v0.60.0 for govulncheck 2026-10-11 21:53:51 +02:00
go.sum build(deps): go 1.26.9 and x/net v0.60.0 for govulncheck 2026-10-11 21:53:51 +02:00
Makefile fix(ci): unblock the Go toolchain, install Trivy, clear the OSV findings 2026-08-09 00:09:59 +02:00
osv-scanner.toml fix(deps): bump x/crypto past the fixed advisories 2026-09-18 21:09:50 +02:00
PUBLIC_SHARE.md feat(backing): draft stems until an admin publishes the setup 2026-09-17 21:18:11 +02:00
README.md feat(remote): control a playing session from another device 2026-10-11 22:37:46 +02:00
renovate.json CI 2026-06-27 14:17:24 +02:00

VibeScore

Indexes a collection of Guitar Pro (.gpx / .gp / .gp5) files and renders each one as a simplified lead sheet: chord diagrams, a bar grid, and chords aligned above the lyrics — all colored by scale degree.

VibeScore screenshot

Features

  • Search: title, artist, album, tempo, time signature, key, tags — with facets, sorting, pagination, and a "Starred" tab.
  • Pattern library: bass/drum patterns under _patterns/<type>/ are indexed apart from the songs: kept out of the library and its filters.
  • Public read-only sharing: /share/<token> links (chart + playback only). See PUBLIC_SHARE.md.
  • Multi-user practice state: each user keeps their own starred list, MIDI mix/FX, backing volumes, sync, transpose, markdown note and video memos. Song tags are shared but only the adder can delete theirs. Admins can promote any user's setup as the song's shared default, or publish a draft setup in one click.
  • Key detection inferred from the chords actually played, not the file's notated signature.
  • Song view: scale strip with fingerings, chords colored by degree, bar grid with diagrams lifted from the tab itself, a tablature mode with per-note interval coloring, a rhythm lane in real engraved notes (Bravura font), and the playing techniques stored in the file (hammer-on / pull-off, slides, bends, vibrato, palm mute, let ring, harmonics, grace notes, tapping, accents and staccato).
  • Playback: synth bass, a backing track synced to the bar grid, a meter-aware metronome and count-in (per-bar signatures, dotted pulse in 6/8, 9/8, 12/8), auto-scroll, a floating transport with waveform seek and looping, a 5-band master EQ, karaoke lyric highlighting, transposition.
  • App-wide player and playlist: playback survives navigation; a persistent player bar and playlist popover manage the queue.
  • Remote control: drive a session playing on one device (laptop) from another one logged into the same account (phone). The header's device button lists your open sessions; the controller mirrors the transport, tempo and snapshots, loop, metronome, master EQ and track mute/solo while the audio stays on the target. Relayed by the server over SSE (/api/remote/*), only between devices of the same user.
  • In-browser MIDI synth (SpessaSynth): upload .sf2/.sf3/.dls soundfonts, optional server-side .sf3 compression, a pedalboard FX chain per channel (compressor, Tube Screamer, amp sim with Fender Deluxe Reverb / Blues Junior / Vox AC30 / Marshall Plexi voicings and cabinet, modulation, EQ, delay, reverb), named presets (save, rename, overwrite, delete). The instrument picker applies each pick live and stays open; mixer edits are kept only once saved ("Save mix"), a reload brings back the saved mix.
  • Multitrack import: a multichannel file (.ogx, unencrypted Rock Band .mogg, any audio with more than 2 channels) is split server-side with ffmpeg into one stem per instrument: silent channels dropped, stereo pairs kept together, stems named by channel for renaming.
  • Stem archives: a .zip / .tar.gz of audio files uploads as one track per supported file (named after it); other members are skipped.
  • Per-channel MP3 overrides: bind a backing stem to a MIDI channel, synced and loudness-matched.
  • Global tempo map: a waveform sync editor (offset/tempo/section markers, START/END points) drives MIDI and MP3 playback together.
  • Per-song extras: favourite, bar annotations, fingering overrides, display transposition, markdown note.
  • MIDI generation on demand via MuseScore when no .mid exists.
  • Mobile-friendly, light/dark theme, download original source file.
  • Extensible formats: container decoding (BCFZ/BCFS/zip/raw gpif) and GPIF parsing 100% in Go — single static binary, no cgo.

Install

Requires Go ≥ 1.26 (pinned in go.mod) and Node ≥ 18.

Optional, feature by feature: MuseScore for .gp* → .mid generation (--mscore) and ffmpeg for .sf2 → .sf3 compression. Both are detected at startup; missing just disables that feature.

# 1. Build the frontend (embedded into the server binary)
make web

# 2. Index the collection (incremental on later runs)
make index ROOT=~/path/to/GuitarPro DB=vibescore.db

# 3. Run the server (API + UI) on http://localhost:8080
make serve ROOT=~/path/to/GuitarPro DB=vibescore.db

The server needs --root: the database stores only a relative path and reconstructs the absolute path from --root (so the database is portable).

Server flags

Every flag below also reads an environment variable of the same name in caps with underscores where noted; --help lists them all.

Flag Default What it does
--db vibescore.db SQLite index database
--root — collection root (also enables POST /api/reindex)
--addr :8080 listen address
--backing <db dir>/backing uploaded backing stems
--soundfont-dir (SOUNDFONT_DIR) <db dir>/soundfonts uploaded .sf2/.sf3 banks
--uploads-dir (UPLOADS_DIR) <root>/_uploads where UI-uploaded scores land when the root is read-only
--mscore (MSCORE) — MuseScore binary for .mid generation (empty disables it)
--max-upload (MAX_UPLOAD) 2G max request body size — raise it for large soundfonts
--dev off permissive CORS for the Vite dev server

Identity comes from a trusted SSO proxy in front of the app; the header names and the group→role mapping are configurable: --auth-user-header (Remote-User), --auth-name-header (Remote-Name), --auth-groups-header (Remote-Groups), --auth-ro-group / --auth-rw-group / --auth-admin-group (comma-separated, each also an env var) and --auth-logout-url for the link the UI redirects to.

Docker

Dockerfile is multi-stage: it builds the SPA and a static, cgo-free server binary, then copies it into a prebuilt base image (Dockerfile.base: Debian slim + headless MuseScore under xvfb + ffmpeg). Build the base once, then the app:

docker build -f Dockerfile.base -t jcosmao/vibescore-base .   # rarely
docker build -t vibescore .

docker run -p 8080:8080 \
  -v ~/path/to/GuitarPro:/music:ro \
  -v vibescore-data:/data \
  vibescore
  • Mount the collection at /music (read-only is fine).
  • The /data volume holds the index database, uploaded backing stems and uploaded soundfonts.
  • make docker / make docker-base / make docker-all wrap the same builds and pushes (Docker Hub, jcosmao/vibescore).
  • CI builds and pushes the image on every push to master (.forgejo/workflows/ci.yml, under kaniko — the runner has no Docker daemon). The base image has its own manual workflow, .forgejo/workflows/docker-base.yml.
  • First run: the index is empty — click Réindexer in the UI (or POST /api/reindex) to scan the collection.

Frontend development

make dev                 # terminal 1: Go API with permissive CORS (:8080)
cd web && npm run dev    # terminal 2: Vite (proxies /api → :8080)

Checks

make test        # Go test suite
make security    # govulncheck, gosec, golangci-lint, npm audit (mirrors CI)
make smoke       # drives a headless browser through Play, fails on any JS error

make smoke is the only check that executes the page — it indexes testdata/ into a throwaway database, opens a song, clicks Play and fails on any exception. Needs chromium or chrome on PATH (or CHROME=/path/to/binary).

API

REST API under /api. With the server running, the interactive docs (Redoc) are at http://localhost:8080/api/docs, and the raw spec at /api/openapi.yaml. Source schema: internal/api/openapi.yaml.