Visual Click Track

Project tracker

The living record of the project: what each feature is, what's done, open issues and questions, and what's left. Update it in the same commit as the work it describes.

Status key: βœ… done Β· 🚧 in progress Β· ⏳ not started Β· πŸ’€ deferred

Issues and questions have stable IDs (ENG-Q1, PLAT-Q1, …) so commits and PRs can refer to them. When one is resolved, move it to the feature's Decisions list with the answer rather than deleting it.

Overview

A screen-based click track for worship teams: a display shows the current section, bar and beat, and a countdown to the next section, with an optional voice announcing sections. Songs are written in the .vct text format (format.md). How the packages, clocks and flows fit together is drawn in architecture.md.

# Feature Status Where
1 Track format spec βœ… v0.1 docs/format.md, docs/guide.md
2 Parser and timeline builder βœ… vct/parse.odin, vct/build.odin
3 CLI βœ… cmd/vct/
4 Tests and CI βœ… tests/, .github/workflows/test.yml
5 Runtime engine 🚧 vct/engine.odin (merged, #2)
6 Player platform and app shell 🚧 cmd/player/, player/, packaging/macos/, scripts/package-macos.sh
7 Audio click output 🚧 optional (K) β€”
8 Voice cues ❌ β€”
9 Screen display 🚧 cmd/player/stage.odin, cmd/player/ui.odin, cmd/player/displays*.odin, cmd/player/look.odin, cmd/player/chart.odin, player/layout.odin, player/chart.odin, player/theme.odin, player/look.odin, player/displays.odin, layouts/, themes/
10 Operator controls 🚧 cmd/player/main.odin
11 Backing track sync 🚧 align/, vct align in cmd/vct/
12 C ABI / ports πŸ’€ β€”
13 Songs, sets and the library 🚧 player/library.odin, vct/set.odin, cmd/player/home.odin, preplay.odin, set_edit.odin
14 Deferred format features πŸ’€ docs/format.md Β§11
15 Ultimate Guitar import βœ… ug/, cmd/ug2vct/
16 Lyrics 🚧 vct/parse.odin, vct/build.odin, player/view.odin, cmd/player/stage.odin
17 Song editor 🚧 vct/lex.odin, player/buffer.odin, player/editor.odin, player/doc.odin, player/complete.odin, cmd/player/edit.odin, cmd/player/edit_form.odin
18 Video render βœ… cmd/player/render.odin
19 Stage sync 🚧 stagesync/, cmd/player/sync.odin, cmd/player/clients.odin, cmd/vct-sync/ (on by default, Shift+S; UDP + WebSocket + web client; connected screens and host permission, C; docs/sync.md)
20 Web version 🚧 web/, scripts/build-web.sh, cmd/player/main_web.odin, player/file_js.odin
21 Language server 🚧 lsp/, cmd/vct-lsp/, editors/, vct/format.odin (formatting)
22 ChordPro import and export βœ… chordpro/, cmd/chordpro/
23 Chord sheet conversion βœ… sheet/, cmd/sheet/
24 Import and export in the player 🚧 sheet/text.odin, player/convert.odin, cmd/player/transfer.odin
25 Score import (MusicXML, MuseScore, MIDI) 🚧 score/, cmd/score2vct/, player/convert.odin
26 Section names from the words βœ… sections/, score/write.odin, sheet/text.odin
27 Lyrics and chord sources (online APIs) ⏳ β€”
28 Mobile apps (iOS, Android) πŸ’€ researched docs/mobile.md
29 Layout and theme editor βœ… player/design.odin, player/layout_edit.odin, player/theme_edit.odin, cmd/player/design*.odin, player/layout.odin (the grid), player/look.odin (the library's folders), layouts/, themes/

Next up (aim: use it live): work through live-checklist.md β€” what needs hands, hardware or a room: dragging files in, the Launchkey pads, the stage displays on a second monitor (DISP-I4), readability at a distance (DISP-Q1), then a rehearsal pass with a monitor and synced phones, one of them with host permission (SYNC-I10). What a Mac could check on its own was done on 2026-10-10: the .app builds and opens a double-clicked song from cold (APP-I5, which froze before), Retina, fullscreen, the audio picker and FIND (EDIT-I5), the set editor's chips (SET-I10) and the Documents library (SET-I1). Release work (notarisation, CI on macOS, web hosting) waits until after live use.

Branches: none open; everything is merged to main.


1. Track format spec

βœ… v0.1 β€” docs/format.md, docs/guide.md, examples/example-song.vct

Done

Issues / questions

Planned (agreed in the v0.2 design interview)

Issues / questions

Remaining

3. CLI

βœ… β€” cmd/vct/main.odin

Done

Remaining

4. Tests and CI

βœ… β€” tests/, scripts/bless.sh, .github/workflows/test.yml

Done

Remaining

5. Runtime engine

🚧 β€” vct/engine.odin, tests/engine_test.odin (merged to main in berryp/visual-click-track#2)

The transport: plays a built Track against the host's clock. Pure logic, pull-based engine_poll(until) with a lookahead; events carry everything the screen needs.

Done

Decisions (signed off October 2026; written into format.md Β§10.1)

Remaining

6. Player platform and app shell

🚧 β€” cmd/player/main.odin (raylib app), cmd/player/open_darwin.odin (Finder open-file), player/ (testable core: driver.odin, view.odin, ring.odin), tests/player_test.odin, packaging/macos/ and scripts/package-macos.sh, scripts/notarize-macos.sh (the macOS app)

Done

Decisions

Issues / questions

Remaining

7. Audio click output

🚧 β€” off by default. K turns an audible click on or off (any screen, Ctrl/Cmd+Shift in the editors, like C and M). cmd/player/click.odin makes two short sine ticks at startup (1.6 kHz on a bar's first beat, 1 kHz otherwise) and plays one through raylib when a beat that has the engine's click flag reaches the screen, so quiet and tacet stay silent. It is timed by the display, not the audio clock, so it is not sample-accurate, and it isn't sent to synced screens or played on the web.

8. Voice cues

❌ Removed from the player β€” it has no voice output and no VOICE caption. The engine still emits Event.say and the format keeps say=; the player ignores them.

9. Screen display

🚧 β€” cmd/player/stage.odin (widgets), cmd/player/ui.odin (canvas, text, tags, numbers, shapes, the theme's palette and fonts), player/view.odin (state), player/layout.odin and layouts/*.json5 (layouts), player/theme.odin, player/ttf.odin and themes/*.json5 (themes), player/look.odin (settings file, finding themes and layouts), player/displays.odin (which layout each display shows), cmd/player/look.odin (loading, applying and reloading them)

Implements the "Stage Display" prototype (https://claude.ai/artifact/DsY6wKttw2xTF8doGxnAWB), keeping its 1920Γ—1080 coordinates, palette (black, mauve, cyan, pink, red) and Barlow type.

design-research.md (2026-10-10) collects prior art (AbleSet, MultiTracks, ProPresenter, OnSong, Ontime and others), what musicians ask for on forums, legibility and timing ideas from other fields, and a ranked list of what to add, compared with what the stage does now. Reddit couldn't be reached and isn't in it.

design/stage.pen (Pen, 2026-10-10) mocks up the play screens with the UX review's fixes (DISP-D11): the stage widgets as components, Band in eight states (playing, countdown, count-in, open repeat, hold, song ended, no words in 3/4, the operator's window), Drums, Vocals, Words & chords and Chart, the before-the-set card on a stage display, and two phone screens, each captioned with the UX-n findings it answers. Default theme only.

Decisions

Done

Decisions

Issues / questions

Remaining

10. Operator controls

🚧 β€” cmd/player/main.odin

Done

Issues / questions

Remaining

11. Backing track sync

🚧 β€” audio and audio-offset headers are reserved in the format; beat detection finds the offset (align/, vct align).

Done

Not done (multi-stem)

Issues / questions

Remaining

12. C ABI / ports

⏳ β€” the core's public types are already flat and C-shaped.

Not needed: the desktop player is written in Odin and imports vct directly (PLAT-Q5). Revisit for mobile or web.

Remaining

13. Songs, sets and the library

🚧 β€” player/library.odin (library, sessions), player/setlist.odin (entries, summary), vct/set.odin (set files), cmd/player/home.odin (home screen), cmd/player/preplay.odin, cmd/player/set_edit.odin; tested in tests/library_test.odin and tests/player_test.odin

Songs and sets are plain files in the user's Documents folder (~/Documents/StageDisplay/{songs,sets}; --library DIR overrides; on the web, /data/StageDisplay). The library is whatever is in those folders; the old cache (vct-player/setlist.txt in the config folder) is gone.

Done

Issues / questions

Decisions

Remaining

14. Deferred format features

πŸ’€ β€” format Β§11: swing/subdivisions, click sound choice, compound-meter pulse (pulse=). Arrangement lists and repeat groups shipped in v0.2 (feature 1, format Β§12).

15. Ultimate Guitar import

βœ… β€” ug/convert.odin (meta JSON to .vct, no I/O), cmd/ug2vct/main.odin (the CLI), tests/ug_test.odin with a made-up fixture in tests/ug/meta.json

Done

Issues / questions

Decisions

Remaining

16. Lyrics

🚧 β€” format Β§5.1; vct/parse.odin (parse_lyric), vct/build.odin, vct/phrase.odin and vct/reflow.odin (grouping lines so they can be read; Β§25), player/view.odin (lyric, view_next_lyric), cmd/player/stage.odin (draw_lyrics); branch claude/lyrics-support-b462af

Decisions

Done

Issues / questions

Remaining

17. Song editor

🚧 β€” vct/lex.odin (line lexer and line formatters), player/buffer.odin (text buffer, undo), player/editor.odin (model, new-song template), player/doc.odin (structured edits), player/complete.odin (autocomplete), cmd/player/edit.odin (screen, text view, song map, preview), cmd/player/edit_form.odin (structured view); tests/editor_test.odin.

An editor for .vct files inside vct-player, with a text view and a structured view of the same file.

Decisions

Done

Issues / questions

Remaining

18. Video render

βœ… β€” cmd/player/render.odin (--render and its options in cmd/player/main.odin), canvas_fit's g_target in cmd/player/ui.odin

Done

Decisions

Issues / questions

19. Stage sync

🚧 β€” stagesync/ (protocol, clock, server, web/WebSocket, client, addresses), stagesync/web/index.html (web client), cmd/player/sync.odin sync_style (the theme and phone layout), qr/ (QR codes), cmd/player/sync.odin, cmd/player/phone.odin (address and QR code), cmd/player/clients.odin (connected screens and host permission), cmd/vct-sync/, docs/sync.md, tests/sync_test.odin

Lets other screens on the network (a phone or tablet browser, an app, another computer) follow the player. The host is in charge and clients are dumb: each message is the whole stage at one moment, stamped with the server time it happens at. Clients don't read songs or run the engine. Pure Odin (core:net).

Decisions

Done

Issues / questions

Remaining

20. Web version

🚧 β€” scripts/build-web.sh (mise run build:web, mise run web to serve), web/index.html, cmd/player/main_web.odin, cmd/player/open_js.odin, cmd/player/midi_js.odin, cmd/player/sync_js.odin, player/file_js.odin, player/arena_js.odin

Done

Issues / questions

Remaining

21. Language server

🚧 β€” lsp/ (server.odin: JSON-RPC dispatch and documents; features.odin; types.odin), cmd/vct-lsp/ (stdio framing), editors/nvim/, editors/zed/, editors/README.md, vct/format.odin (formatter); tests/lsp_test.odin, tests/format_test.odin. Merged in #56.

vct-lsp, an LSP server so .vct songs can be written in Neovim, Zed or any other LSP editor with the same checks and help as the built-in editor.

Decisions

Done

Issues / questions

Remaining

22. ChordPro import and export

βœ… β€” chordpro/import.odin (ChordPro to .vct), chordpro/export.odin (.vct to ChordPro), both with no I/O; cmd/chordpro/main.odin (the CLI); tests/chordpro_test.odin with fixtures in tests/chordpro/

Done

Issues / questions

Remaining

23. Chord sheet conversion

βœ… β€” sheet/sheet.odin (the parser, and .vct by way of ChordPro), sheet/gp5.odin (Guitar Pro 5), both with no I/O; cmd/sheet/main.odin (the CLI); tests/sheet_test.odin with a fixture in tests/sheet/

Done

Issues / questions

Remaining

24. Import and export in the player

🚧 β€” sheet/text.odin (sheet ⇄ ChordPro, no I/O, beside Β§23's parser), player/convert.odin (format detection, import into the library, export), cmd/player/transfer.odin (the dialogs), the pickers in cmd/player/open_*.odin; tests/sheet_test.odin with fixtures tests/sheet/song*

Done

Issues / questions

Remaining

25. Score import (MusicXML, MuseScore, MIDI)

🚧 β€” score/ (no I/O: zip.odin, xml.odin, a reader per format in musicxml.odin, mscx.odin, midi.odin, and write.odin for the .vct), vct/phrase.odin (grouping the words into readable lines) and vct/reflow.odin (the same on a .vct already written), cmd/score2vct/main.odin (the CLI), formats .MusicXML, .MuseScore, .MIDI in player/convert.odin; tests/score_test.odin with fixtures in tests/score/ (the same song as .musicxml, .mxl, .mscx and .mscz; the MIDI file is built in the test)

Done

Decisions

Issues / questions

Remaining

26. Section names from the words

βœ… β€” sections/sections.odin (the detector, no I/O), used by score/write.odin (name_sections) and sheet/text.odin (with_sections, called from player/convert.odin); tests/sections_test.odin with the fixture tests/sections/falling-slowly-lyrics.txt

Done

Decisions

Issues / questions

Remaining

27. Lyrics and chord sources (online APIs)

⏳ β€” nothing built. Research for fixing imported words and filling in chords, done October 2026. Checked by hand with curl; nothing here is in the app.

What's out there

Suggestions, in the order they'd pay off

  1. Fix the words from LRCLIB in the editor (free, no key, no account): look the song up by title and artist, then match each of our lyric lines to the fetched ones with the same word overlap Β§26 uses, and offer the changes. It would clean up what a score's syllables give (wright my name, cherryred, survive the fall) and what a search result mangles, and the blank lines in plainLyrics feed Β§26's split straight away, so the parts get named as well.
  2. Time the lyrics from syncedLyrics: with the song's tempo (or the offset Β§11's align finds), [mm:ss.xx] becomes bar.beat, which is exactly what a " line wants. That would fix the long-and-short phrases a score gives (SCORE-I3) and give lyrics to a MIDI or a chord sheet that has none.
  3. Make it a source, not a dependency: one small package (no I/O, as with ug), one CLI to try it with, cached on disk, and every import working as it does now when the network is away or the song isn't there. Send a User-Agent that names the app, back off on 429.
  4. Ask before fetching: looking a song up sends its title and artist to someone else. Worth a setting that's off by default, and a line in the README about where the words come from and that LRCLIB's are crowd-sourced (so not authoritative) β€” and that the song's own licence (CCLI for a worship team) is the user's to hold.
  5. Later, if wanted: Klangio for chords from a backing track (it writes MusicXML, so Β§25 would read the result), or a SongSelect partnership for a licensed source with section names and charts.

Issues / questions

28. Mobile apps (iOS, Android)

πŸ’€ β€” researched October 2026, no code, and parked: not a near-term requirement. The findings keep: they say what to do when it comes back, and MOB-D1 (keep the wasm build) is settled either way. The research is written up in mobile.md: what already ports, why raylib is the blocker, the backend comparison, why wasm isn't the way in, the recommended architecture, a feature-by-feature compatibility table and what the shells have to bridge.

The goal: the native desktop and mobile apps are thin shells that embed one reusable player and bridge it to the system.

Done

Decisions

Issues / questions

Remaining

29. Layout and theme editor

βœ… β€” player/design.odin (the browse screen's model: listing, names, copying, renaming, deleting), player/layout_edit.odin (the layout editor's model: selection, undo, dragging on the grid), player/theme_edit.odin (the theme editor's model: fields, undo, writing with relative fonts), cmd/player/design.odin (the screen), cmd/player/design_layout.odin (the layout editor), cmd/player/design_theme.odin (the theme editor), cmd/player/design_preview.odin (the editors' preview), player/layout.odin (LAYOUT_GRID, grid_snap, grid_snap_box, layout_write), player/theme.odin (theme_write), player/look.odin (the library's folders), layouts/*.json5, themes/*.json5

Lay out the stage by dragging widgets rather than by editing JSON5 by hand: a browse screen listing every layout and theme, a canvas where widgets are moved and resized on a grid, a panel for the selected widget's settings, and a theme editor for the colours, sizes and fonts β€” with the real stage drawing underneath, so what the screen shows is what the stage will show.

Decisions

Done

Issues / questions

Remaining