Problem
The projector currently shows a full verse at once. On large screens this results in small text that is hard to follow. A line-by-line mode — showing 1 or 2 lines at a time — keeps the display clean, the text large, and the congregation focused.
Display layout
Three layers rendered simultaneously on the projection screen:
Early in the morning our song ← previous line, fading out upward, dimmed
Holy, holy, holy Lord ← CURRENT line, full size, centred
Heilig, heilig, heilig, Herr Gott ← translation subline, smaller
When the morning stars began ← next line, barely visible at bottom
When the operator advances, the current line slides up and the next line enters from below — smooth transition, configurable speed.
Bilingual support
Each song part carries a language tag and a translation_type:
singable — professional translation that fits the melody. Both languages displayed at equal size. The congregation can sing in either language.
Holy, holy, holy, Lord God Almighty
Heilig, heilig, heilig, Herr Gott Allmächtiger
reference — literal translation for comprehension only. Cannot be sung to this melody. Displayed small and dimmed below the main line.
Holy, holy, holy, Lord God Almighty
ₛ Heilig, heilig, heilig, Herr Gott Allmächtiger
Language role switching
The operator can flip which language is primary at any moment during the service — no reload, instant sync to the display window:
- 🇬🇧 primary → 🇩🇪 reference below
- 🇩🇪 primary → 🇬🇧 reference below
Presenter dashboard
┌─────────────────────────────────────┐
│ Verse 2 · line 3 / 5 │
│ │
│ [ NOW ] │
│ Holy, holy, holy Lord │
│ │
│ [ NEXT ] │
│ Early in the morning our song │
│ │
│ ← Back Forward → [B] Blank │
│ │
│ 🇬🇧 Primary | 🇩🇪 Reference [⇄] │
│ Lines: [1] [2] A− A+ │
└─────────────────────────────────────┘
Keyboard shortcuts
| Key |
Action |
← → |
previous / next line |
B |
blank screen |
F |
request fullscreen on display window |
1 / 2 |
switch 1 or 2 lines per slide |
DB changes required
- Add
language VARCHAR to song_parts
- Add
translation_type ENUM('original', 'singable', 'reference') to song_parts
- New migration in
packages/db/migrations/
- Admin UI: language + type selectors per song part
SongPartDto extended with language and translationType
BroadcastChannel — new message types
| type |
direction |
payload |
lineIndex |
both |
{ partIndex, lineIndex } |
lineMode |
presenter → display |
{ active, linesPerSlide: 1|2 } |
secondaryLang |
presenter → display |
{ lang: string | null } |
primaryLang |
presenter → display |
{ lang: string } |
blank |
presenter → display |
{ active: boolean } |
transition |
presenter → display |
{ style: 'fade'|'slide', durationMs: number } |
Acceptance criteria
App: Songbook (songs.sdarm.life)
Severity: 🟠 High
Problem
The projector currently shows a full verse at once. On large screens this results in small text that is hard to follow. A line-by-line mode — showing 1 or 2 lines at a time — keeps the display clean, the text large, and the congregation focused.
Display layout
Three layers rendered simultaneously on the projection screen:
When the operator advances, the current line slides up and the next line enters from below — smooth transition, configurable speed.
Bilingual support
Each song part carries a
languagetag and atranslation_type:singable— professional translation that fits the melody. Both languages displayed at equal size. The congregation can sing in either language.reference— literal translation for comprehension only. Cannot be sung to this melody. Displayed small and dimmed below the main line.Language role switching
The operator can flip which language is primary at any moment during the service — no reload, instant sync to the display window:
Presenter dashboard
Keyboard shortcuts
← →BF1/2DB changes required
language VARCHARtosong_partstranslation_type ENUM('original', 'singable', 'reference')tosong_partspackages/db/migrations/SongPartDtoextended withlanguageandtranslationTypeBroadcastChannel — new message types
lineIndex{ partIndex, lineIndex }lineMode{ active, linesPerSlide: 1|2 }secondaryLang{ lang: string | null }primaryLang{ lang: string }blank{ active: boolean }transition{ style: 'fade'|'slide', durationMs: number }Acceptance criteria
Line-by-linetoggle switches mode live without reloadsingabletranslations shown full-size alongside main linereferencetranslations shown small and dimmed belowPart · line X / Nindicatorlanguageandtranslation_typetosong_parts?screenshot=1/?projector=1modes unaffectedApp: Songbook (songs.sdarm.life)
Severity: 🟠 High