Waiting for the shuffle bag to land on the animation you are working on makes
testing one of them tedious. The queue page now carries an Animation row next
to Background: Auto keeps the per-track cycle, any other button pins the player
to that animation until the mode changes again.
The animation names live in config.js alongside their labels, so the server can
validate them and the queue page can label its buttons; motion.js keys its
registry by the same names.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tg15tLAuT6NrCE1ZxKVgDB
Two frontends sharing one WebSocket-backed state on the server:
- Player: fullscreen poster background with a small frame (30% of the
screen) that drifts diagonally and reflects off the edges. Two stacked
YouTube players hand over with a 5s equal-power crossfade so there is
never silence between tracks.
- Queue: add by URL, drag to reorder, delete, click to jump. Shows title
and length per item, highlights what is playing, and tracks remaining
vs. total playtime.
No API key is required — titles come from YouTube's oEmbed endpoint and
durations from a hidden muted probe player that cues each new video just
to read its length.
The fade is driven by both requestAnimationFrame and the interval tick:
a hidden or throttled tab stops serving frames entirely, which would
otherwise strand a handover half-faded with two tracks audible.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tg15tLAuT6NrCE1ZxKVgDB