Shorts Jam: The Story Behind Synchronized Social Watching

Inspiration

The idea for Shorts Jam was born from a simple observation: we're more connected than ever, yet we scroll alone.

During the pandemic, we watched friends and family consume endless hours of YouTube Shorts in isolation. While the algorithm is great at personalizing content, it creates echo chambers and eliminates the shared experience of discovering content together. We asked ourselves: What if watching short-form content could be as social as watching a movie together?

Traditional watch parties focus on long-form content, but short-form videos present unique challenges: rapid content switching, diverse preferences, and the need for real-time synchronization. We wanted to solve this.

What We Learned

Technical Discoveries

  1. Real-time Synchronization is Hard: Keeping multiple users perfectly in sync required deep understanding of WebSocket architecture and state management. We learned to handle race conditions, network latency (approx 50-200ms), and graceful degradation.

  2. YouTube IFrame API Mastery: The auto-advance feature required mastering YouTube's Player API, understanding state change events, and handling edge cases like failed loads and network interruptions.

  3. Database Design for Real-time: Structuring Supabase tables with proper RLS policies while maintaining real-time subscriptions taught us the balance between security and performance.

Soft Skills

  • Minimalist Design Philosophy: Less is more. We learned to ruthlessly cut features that didn't serve the core experience.
  • User-Centric Thinking: Every decision was tested against "Does this make watching together easier?"

How We Built It

Architecture Overview

┌─────────────┐
│   React UI  │
│  (TypeScript)│
└──────┬──────┘
       │
       ├──── Supabase Realtime ────┐
       │     (WebSocket)            │
       │                            ▼
       │                   ┌───────────────┐
       │                   │   Database    │
       │                   │  - sessions   │
       │                   │  - participants│
       │                   │  - queue      │
       │                   │  - votes      │
       │                   │  - chat       │
       │                   └───────────────┘
       │
       └──── YouTube IFrame API
             (Video Playback)

Tech Stack

  • Frontend: React 18 + TypeScript + Vite
  • Styling: Tailwind CSS + shadcn/ui components
  • Backend: Supabase (PostgreSQL + Real-time + Edge Functions)
  • Video: YouTube IFrame Player API
  • State Management: React hooks + Supabase subscriptions

Key Features Implementation

1. Smart Algorithm Rotation

// Probability of playing from feed vs queue
P(queue) = queue.length > 0 ? 1.0 : 0.0
P(feed) = 1.0 - P(queue)

// Round-robin feed selection
next_user_index = (current_index + 1) mod total_participants

2. Real-time Presence Tracking

Used Supabase Presence API to track who's actively watching:

  • Updates every 30 seconds
  • Graceful handling of disconnections
  • Visual indicators for active participants

3. Synchronized Playback

// State synchronization formula
video_time = start_timestamp + (current_time - playback_started_at)
sync_offset = |client_time - video_time|

if (sync_offset > 2000ms) {
  resync_player()
}

Challenges We Faced

Challenge 1: Race Conditions in Vote Counting

Problem: Multiple users voting simultaneously caused incorrect skip counts.

Solution: Implemented optimistic UI updates with PostgreSQL's UPSERT pattern:

INSERT INTO votes (user_id, session_id, vote)
VALUES ($1, $2, $3)
ON CONFLICT (user_id, session_id) 
DO UPDATE SET vote = $3, updated_at = NOW();

Challenge 2: Auto-Advance Without Infinite Loops

Problem: The onNextVideo callback in the YouTube player event listener would sometimes trigger multiple times, creating a cascade effect.

Solution: Added cleanup logic and proper dependency arrays in React's useEffect, ensuring the player destroys and recreates cleanly on video changes.

Challenge 3: Feed Depletion

Problem: What happens when a user's feed runs out?

Solution: Created a fallback placeholder feed generator and automatic feed rotation. When a feed ends, we move to the next participant's feed:

const handleFeedDepletion = (currentUserId) => {
  const nextUser = participants.find(
    p => p.id !== currentUserId && hasFeedItems(p)
  );
  return nextUser || generatePlaceholderFeed();
}

Challenge 4: Mobile Responsiveness

Problem: YouTube's 9:16 aspect ratio videos needed to work perfectly on all screen sizes.

Solution: CSS aspect-ratio property combined with responsive containers:

aspect-ratio: 9/16;
max-width: min(100%, 448px);

Challenge 5: No-Auth Instant Join

Problem: Traditional authentication flows create friction. We wanted instant access.

Solution: Implemented a participant-based system with 6-character join codes. Each participant gets a UUID stored in URL params—no email, no password, just instant connection.

Future Improvements

  • Content filtering: Age ratings and category preferences
  • Playlist mode: Pre-curated watch parties
  • Reactions: Real-time emoji reactions synchronized with video playback
  • Analytics: Track watch history and popular videos
  • Mobile app: Native iOS/Android experience

What We're Proud Of

We built a fully functional, real-time social experience from scratch in record time. Shorts Jam proves that technology can enhance human connection rather than isolate us. Every feature was designed with one question: "Does this bring people closer together?"

The answer? Yes.


Try it yourself: Create a jam session and share the code with friends. Experience the joy of watching together.

Create About/Story Page Create Demo Video Section

Built With

Share this project:

Updates