Real‑time YouTube watch parties with synchronized playback, chat, and host controls, built with Next.js, Socket.IO, and Redis.
- Synchronized playback: Host controls play, pause, and seek; all guests stay in sync using periodic sync checks.
- YouTube support: Paste any YouTube or
youtu.beURL; only the host can change the video. - Room system:
- Create a room with a short 6‑character room ID.
- Join a room as host (with a host token) or as a guest with a validated display name.
- Rooms and chat history are stored in Redis with automatic cleanup.
- Real‑time chat:
- Live chat per room with typing indicators.
- Messages persisted in Redis (last 20 messages per room).
- Host tools:
- Promote guests to host.
- Automatic room closing when all hosts leave (guests are redirected home).
- Polished UI:
- Landing page with hero, features, integrations, and CTA.
- Room layout with video player, user list, sidebar chat, and fullscreen chat overlay.
- Light/dark mode with mode‑specific hero screenshots.
- Frontend
- Next.js App Router (custom server via
server.ts) - React, TypeScript, Tailwind CSS
socket.io-clientfor real‑time events- Zod for schema validation on the client
- Next.js App Router (custom server via
- Backend
- Custom Next.js HTTP server + Socket.IO (
src/backend/socket) - Redis (local or Upstash) for room and chat persistence (
src/backend/redis) - Zod‑validated Socket.IO payloads
- Custom Next.js HTTP server + Socket.IO (
- Tooling
- Bun / Node.js for scripts
- ESLint, Prettier, Tailwind CSS v4
- Docker (Dockerfile) for production deployments
- Node.js 20+ (recommended)
- Bun installed (
bunCLI) or any Node package manager (npm/pnpm/yarn) - Redis:
- Local Redis instance (e.g.
redis-serveror Docker), or - Upstash Redis URL
- Local Redis instance (e.g.
Using Bun (recommended):
cd watchwithme
bun installUsing npm:
cd watchwithme
npm installCreate a .env.local file in the project root (or set env vars in your shell/hosting platform):
- Required
REDIS_URL: Redis connection string- Local example:
redis://localhost:6379 - Upstash example:
rediss://default:<password>@<your-subdomain>.upstash.io:6379
- Local example:
- Optional (for production CORS)
ALLOWED_ORIGINS: Comma‑separated list of allowed origins for Socket.IO, e.g.
https://watchwithme.app,https://www.watchwithme.app
If REDIS_URL is not set, the app defaults to redis://localhost:6379.
The app uses a custom Next.js server (server.ts) that also hosts Socket.IO.
# Dev (Bun)
bun run dev
# Or with npm
npm run devThen open http://localhost:3000 in your browser.
If you are using local Redis via Docker:
docker run --name watch-with-redis -p 6379:6379 -d redis:7-alpineThe repo ships a single Dockerfile. Redis is not bundled in the image — point REDIS_URL at a Redis instance (Coolify Redis service, Upstash, or a local container).
Coolify: Build pack Dockerfile, port 3000, static site off. Add env vars from .env.example (especially REDIS_URL and ALLOWED_ORIGINS).
Local app + Redis with plain Docker:
docker build -t watchwithme .
docker network create watchwithme-net 2>/dev/null || true
docker run -d --name watch-redis --network watchwithme-net -p 6379:6379 redis:7-alpine
docker run -d --name watchwithme --network watchwithme-net -p 3000:3000 \
-e NODE_ENV=production \
-e REDIS_URL=redis://watch-redis:6379 \
-e ALLOWED_ORIGINS=http://localhost:3000 \
watchwithmeHealth check: GET /health on port 3000.
- Custom server (
server.ts)- Creates an HTTP server around Next.js.
- Attaches Socket.IO at path
/api/socket/io. - Exposes
/healthfor health checks.
- Socket.IO layer (
src/backend/socket)initSocketIOsets up the Socket.IO server with CORS, then:registerRoomHandlers: create/join/leave rooms, host promotion, room closing.registerVideoHandlers: set video URL, play/pause/seek, periodic sync checks.registerChatHandlers: send messages, typing indicators.
- All events are validated with Zod before being processed.
- Redis layer (
src/backend/redis)RoomRepository:- Stores rooms with TTL and tracks active rooms.
- Manages users, host assignment, and video state.
ChatRepository:- Stores last 20 chat messages per room with TTL.
RedisService:- Singleton that exposes
roomsandchatrepositories.
- Singleton that exposes
- Client layer
SocketProvider(src/contexts/socket-provider.tsx) creates a shared Socket.IO client connected to/api/socket/io.- Hooks:
useCreateRoom/useJoinRoom: form handling and socket events for creating/joining rooms.useRoom: manages room state, users, chat, typing, and error handling.useVideoSync: coordinates YouTube player state with socket events and host sync checks.
- UI:
- Landing page: marketing/hero + CTA.
- Room page: YouTube player, user list, chat sidebar, and fullscreen chat overlay.
bun run dev/npm run dev: Start custom Next.js dev server (tsx server.ts).npm run build/bun run build: Runnext build.npm start/bun start: Start the custom server in production mode.npm run lint: Run ESLint.
server.ts: Custom Next.js + Socket.IO HTTP server.src/app:(landing): Marketing pages and layout.room/[roomId]: Room page with video player and chat.
src/backend/socket: Socket.IO server event handlers and types.src/backend/redis: Redis client + repositories for rooms and chat.src/contexts: Providers (Socket, Theme).src/hooks: Client hooks for room, video sync, and room creation/joining.src/components: UI components for landing, room, chat, and video.
This README is specific to this project; update it as the architecture or deployment strategy evolves.