What I Learned Building a Synchronized Social Music Room

0
3
Asked By MellowPine_47 On

I recently built a social music site inspired by the old virtual DJ-room format, where people join rooms, queue songs, and listen together in sync. A group of internet friends used to do this every week, and after the service disappeared I decided to build my own alternative.

The frontend uses React 18 and Vite. The backend is Express with Socket.IO, PostgreSQL, and Redis, all written in plain JavaScript. I skipped TypeScript, state-management libraries, CSS frameworks, and a dedicated test runner in favor of keeping the system small and straightforward.

Playback synchronization has been the most involved part. Each client embeds its own YouTube player, while the server remains authoritative over the current video and its start time. Redis stores the active playback state for each room. Clients estimate their clock offset from server timestamps, calculate the correct playback position, and seek their local player accordingly. Tracks are scheduled roughly two seconds in the future so clients have time to buffer, and clients periodically correct drift if they fall several seconds behind. Only the server advances the queue, which avoids clients disagreeing about what should play next.

I also built a synchronized pixel-art light show. Since the audio is inside a cross-origin iframe, the site cannot analyze it directly. Instead, every client generates the same visuals from the play ID and playback position using a seeded PRNG. Votes and reactions can add visual layers without requiring additional synchronization traffic.

Playback failures are handled with multiple signals. Clients report iframe errors, stalls, and successful playback. A video is skipped after enough independent failures, while failures without a specific error code are treated as potentially local browser problems. Repeated failures can also remove a DJ from the rotation. Presence is reference-counted per connection so multiple tabs work correctly, and queue updates are protected by locks.

Redis stores ephemeral data such as presence, queues, votes, chat history, and playback state, while PostgreSQL stores data that needs to survive restarts. Queue positions are preserved through disconnect grace periods and deployments. The site has no cookies, trackers, or analytics, and the interface uses inline or generated visual assets rather than binary files. There is also a token-authenticated bot API.

I would appreciate experienced feedback on the architecture, development process, and especially synchronization behavior under flaky connections. I am particularly interested in edge cases involving restarts, failed embeds, clock drift, and scaling across multiple application instances.

4 Answers

Answered By QuietHarbor_5 On

The synchronized playback design is heading in the right direction: a server-owned timeline, clock correction, future start time, and periodic drift correction are the important pieces. I would collect metrics for measured offset, seek corrections, buffering, and time spent waiting for PLAYING. That will help distinguish a genuinely broken video from a user whose browser or connection cannot load the embed. A threshold for failures is much safer than trusting the first report.

Answered By CopperLark8 On

The seeded light-show approach is excellent. Making the visuals a deterministic function of the play ID and track position gives everyone the same result without sending a stream of animation data. That is a very clean solution for a cross-origin player where audio analysis is unavailable.

Answered By DriftwoodNexus3 On

The restart path deserves especially aggressive testing. Redis can preserve the active track, but an in-memory timer disappears when the process dies. On startup, recompute the current position from startedAt, schedule only the remaining duration, and use a Redis lease or fencing token so two instances cannot both advance the queue. I would deliberately terminate the server during the final second of a track and verify that clients receive exactly one transition event.

QuartzMango19 -

This is also where multi-instance deployments can become surprisingly difficult. Pub/sub can distribute events, but it does not by itself guarantee that only one worker owns the rotation transition.

Answered By SableOrbit_62 On

The overall architecture sounds practical, but I would be cautious about skipping TypeScript and shared UI primitives as the project grows. Plain JavaScript and no component library can be perfectly reasonable for a focused build, but the maintenance cost tends to appear later when event payloads and room state become more complicated. Adding runtime validation around Socket.IO messages could give you some of the safety without requiring a full rewrite.

MellowPine_47 -

That was my main concern too. I deliberately kept the first version simple, but I may introduce stronger event validation and reconsider TypeScript if the codebase continues expanding.

Related Questions

LEAVE A REPLY

Please enter your comment!
Please enter your name here

This site uses Akismet to reduce spam. Learn how your comment data is processed.