Felix AnderssonSenior Fullstack Engineer, home

Montevideo

Résumé
All work

03 / 07

PlaySync

Same video, same second, from two different time zones. Create a room, share a code, and watch YouTube together while you chat.

Role
Solo: product, design, frontend and realtime server
Year
2026
Interface
English and Spanish, light and dark

Hosted on a free Render instance, so the first load can take up to a minute while it wakes up.

The story

The story

Everyone’s play, pause and seek stay locked while you chat, react and queue up the next video. The video never touches the server: each client streams straight from YouTube, and only small sync and chat messages go through the room.

The server owns the clock. It stores a position and when it last changed, and clients estimate server time NTP-style, discarding samples from throttled background tabs. A reconcile loop only seeks when drift passes 1.5 seconds, and waits 2.5 seconds after a local action so the player never fights its own echo.

In the screenshot
PlaySync room on desktop: Big Buck Bunny playing with custom controls, the room code, a Watching together status, a chat between Felix and Sofi, and Sofi’s popcorn reaction floating over the video.PlaySync room on desktop: Big Buck Bunny playing with custom controls, the room code, a Watching together status, a chat between Felix and Sofi, and Sofi’s popcorn reaction floating over the video.
  1. Live presence: who is here, and whether the room is watching together.
  2. The room code. No accounts, just share it.
  3. Chat, queue and people share one panel; on phones they become a bottom tab bar.

Engineering

Three things worth a closer look

  1. 01

    Permissions on the server

    Host, control and viewer roles are enforced server-side. The host survives a reload through a per-browser ID and hands over to the longest-present guest after a 60-second grace period.

  2. 02

    A Google Drive streaming proxy

    Gets past Drive’s virus-scan page, forwards Range headers so seeking works, re-resolves expiring URLs, and cuts stalled streams after 12 seconds.

  3. 03

    Hardened by default

    Every intent is validated, chat is rate limited, messages are capped at 16 KB, static serving blocks path traversal, and blocked autoplay on iOS turns into a clear tap-to-play state.

  • React 19
  • TypeScript
  • Vite
  • Node (node:http + ws)
  • YouTube IFrame API
  • PWA with Workbox
  • Render

More screens from PlaySync

Select a screen to enlarge it

Search without an API key
Landing
The guest, on a phone

Build sheet

Stack
React 19, TypeScript, Vite, Node (node:http + ws), YouTube IFrame API, PWA with Workbox, Render
Scale
  • ~8,300 lines
  • One process, no database
  • 18-check two-client smoke test
Typefaces
  • Bricolage Grotesque
  • Figtree
Palette
#0F0B10#FFB08A#FF8497#7FDCAE#F7EEE9