谁该控制房间里的音乐?这问题几乎每场派对、每次宿舍夜谈、每个自习室都会撞上。一个人连上蓝牙音箱,周围的人只能不断递上手机:“下一首切这个” “给我放那首” “这个好听再听一遍”。喊多了,播的人烦,等的人急。我们的团队想了一招:干脆让所有人都能在同一个界面投票,让最受欢迎的歌自动排到播放队列最前面。就这样,DJ ROOTS 诞生了。

它不是一个传统播放器,而是一个完全实时协作的 DJ 台。你打开网页,建一个房间,把六位房间码丢进群聊,所有人瞬间入座。不需要下载任何 App,不用扫二维码跳转,浏览器打开就行。进来的每个人都能用 YouTube 链接或关键词搜歌添歌,接着就能看到整条队列,对每首歌点赞或者踩。每一票都会马上改变队列顺序,所有连接设备同步刷新,根本不用手动刷新页面。房主还多一项超能力——能用摄像头隔空手势控场,手一挥就能切歌,捏一下指尖就能调音量。

我们做这个项目之前,先花了很长时间梳理“群体听歌”这件事到底坏在哪。传统的串流方案里,只有房主有控制权,点歌靠口头喊,很容易被忽略或遗忘;有些软件虽然有合作歌单,但缺乏真正的实时投票机制,也没法直观看到“这屋子的人现在最想听哪首”。更麻烦的是,市面上几乎没有不用安装、随手就能用的网页方案。所以我们定下的目标非常清晰:音乐播放权要民主,体验要零门槛。

整个架构说穿了就三条线:前端的交互与视觉,后端的音频处理与数据,实时通道把两端焊在一起。前端选了 React 19 和 Vite 打底,Tailwind CSS v4 处理整体样式,动画靠 Framer Motion、Three.js、GSAP 和 OGL 一起撑起来,把歌单变化、投票飞入、视觉效果做得很有弹性。后端的 Node.js 加 Express.js 负责处理 YouTube 音源,通过 yt-dlp 和 youtube-dl-exec 从链接里抓出纯音频流,再交给 HTML5 Audio API 和 Web Audio API 在前端播放。至于数据库和鉴权,全部扔在 Supabase 上,同时吃住 PostgreSQL 和 Supabase 自带的认证系统。最关键的一环——实时同步,也是靠 Supabase Realtime 这条通道实现:不管是新歌加入、投票变化还是播放进度,只要数据库里那张表一更新,所有订阅这个房间的客户端瞬间就能拿到最新状态,不用轮询,不用手动刷。

手势控场的部分直接上了 Google MediaPipe 的手势识别器。房主授权摄像头后,系统就能识读出大拇指竖起、拇指向下、捏合指尖和横扫四组指令,正好对应点赞、踩、音量调节和切歌。这样一来,房主不用离开座位翻手机,手一举就能决定下一首歌的命运。整套流程从捕捉手势、解析指令再到驱动播放界面变化,全部在浏览器端完成,只有必要的事件才会通过实时通道同步给其他成员。

我们做功能清单的时候,基本上把“协作”“即时”“直觉化”挂在了最高优先级。协作歌单做到了每人同等的一票权,而不是按进房顺序分权重,最热的歌自动浮上来;实时同步做到了播放入口统一,房主点播放后,后端提取音频推流,每个参与者的页面不但曲目同步,连进度条都在一个点上;手势控制则把“不想碰屏幕”的场景补上了,厨房做饭时、手上沾了面粉时、窝在沙发不想动时,挥挥手就能切歌。三块功能互相咬合,最后给出的体验是:一群人围着一块屏幕决定 BGM,而不是一个人低头做歌单。

现在回看,这个项目最让我们兴奋的地方,不是用了多新的技术,而是把一个老问题拆解得足够干净。群体音乐的控制权从来不是一个技术难题,而是一个产品流程问题。把决定权分布出去,把反馈做到实时,把门槛压到浏览器打开即用,这三样同时成立的时候,音乐才真正变成一群人共享的节奏,而不是某一个人的单人播单。DJ ROOTS 是我们在“把选择交还给群体”这条路上的一次尝试,而这条路还有太多有趣的方向可以继续刨下去。