Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ScreenCast — Instant Screen Sharing

One click. A unique link. Anyone with the link watches your screen live.
WebRTC peer-to-peer · No account · No plugins · No recordings stored.


Architecture

Browser (Host)          Signaling Server (Railway)        Browser (Viewer)
     │                         │                                │
     │── POST /api/rooms ──────►│                                │
     │◄─ { roomId } ───────────│                                │
     │                         │                                │
     │── WS: host:create ──────►│                                │
     │                         │◄─── WS: viewer:join ───────────│
     │◄── viewer:joined ───────│                                │
     │                         │                                │
     │── signal:offer ─────────►│─── signal:offer ──────────────►│
     │◄── signal:answer ────────│◄── signal:answer ──────────────│
     │── signal:ice ────────────►│─── signal:ice ─────────────────►│
     │◄── signal:ice ───────────│◄── signal:ice ──────────────────│
     │                         │                                │
     │◄══════════════ Direct WebRTC P2P stream ═══════════════►│

The signaling server only brokers the WebRTC handshake (offer/answer/ICE).
The actual video stream travels directly peer-to-peer — the server never sees your screen.


Quick Start (Local Dev)

Prerequisites

  • Node.js 18+
  • A modern browser (Chrome, Edge, Firefox, Safari 15+)

1. Install dependencies

# From repo root
npm run install:all

# Or manually:
cd server && npm install
cd ../client && npm install

2. Configure environment

Server:

cp server/.env.example server/.env
# Default values work for local dev — no edits needed

Client:

cp client/.env.example client/.env
# Default values work for local dev — no edits needed

3. Run both services

Open two terminal tabs:

# Terminal 1 — Signaling server (port 3001)
npm run dev:server

# Terminal 2 — React client (port 5173)
npm run dev:client

4. Use it

  1. Open http://localhost:5173
  2. Click "Start Screen Share"
  3. Choose which screen/window/tab to share
  4. Copy the link from the bottom panel
  5. Open the link in another browser tab (or send to someone else)
  6. They see your screen live ✓

Environment Variables

Server (server/.env)

Variable Default Description
PORT 3001 Port the signaling server listens on
CLIENT_URL http://localhost:5173 Allowed CORS origin (your frontend URL)

Client (client/.env)

Variable Default Description
VITE_SERVER_URL http://localhost:3001 URL of the signaling server

Production Deployment

Step 1 — Deploy the Signaling Server to Railway

  1. Create a new project at railway.app
  2. Connect your GitHub repo (or drag-drop the server/ folder)
  3. Set the Root Directory to server
  4. Railway auto-detects Node.js via package.json
  5. Add environment variables in Railway dashboard:
    PORT=3001
    CLIENT_URL=https://your-app.vercel.app
    
  6. Deploy — Railway gives you a public URL like https://screencast-server.up.railway.app

Step 2 — Deploy the Client to Vercel

  1. Push the repo to GitHub
  2. Import the project at vercel.com
  3. Set Root Directory to client
  4. Set Build Command: npm run build
  5. Set Output Directory: dist
  6. Add environment variable:
    VITE_SERVER_URL=https://screencast-server.up.railway.app
    
  7. Deploy — Vercel gives you a URL like https://screencast.vercel.app

Step 3 — Update Railway CORS

Go back to Railway and update:

CLIENT_URL=https://screencast.vercel.app

Redeploy the server. Done ✓


Alternative: Deploy Server to Render

  1. New Web Service → connect repo → Root: server
  2. Build command: npm install
  3. Start command: node index.js
  4. Add env vars: PORT=10000, CLIENT_URL=https://your-app.vercel.app
  5. Free tier has spin-down delay — Railway is recommended for lower latency

Alternative: Deploy Server to Fly.io

cd server
fly launch --name screencast-server
fly secrets set CLIENT_URL=https://your-app.vercel.app
fly deploy

Project Structure

screenshare/
├── package.json              # Root convenience scripts
│
├── server/
│   ├── index.js              # Express + Socket.io signaling server
│   ├── package.json
│   ├── .env.example
│   ├── railway.toml          # Railway deployment config
│   └── Procfile              # Render/Heroku deployment
│
└── client/
    ├── index.html
    ├── vite.config.js
    ├── vercel.json           # Vercel SPA routing
    ├── package.json
    ├── .env.example
    └── src/
        ├── main.jsx
        ├── App.jsx           # Router: / and /room/:roomId
        ├── index.css         # Design system (CSS variables)
        │
        ├── pages/
        │   ├── HomePage.jsx  # Landing page + "Start Screen Share"
        │   ├── HomePage.css
        │   ├── RoomPage.jsx  # Host or Viewer depending on ?host=1
        │   └── RoomPage.css
        │
        ├── components/
        │   ├── HostView.jsx  # Screen preview + share link panel
        │   ├── HostView.css
        │   ├── ViewerView.jsx # Remote stream display + status UI
        │   └── ViewerView.css
        │
        ├── hooks/
        │   ├── useHost.js    # Host WebRTC logic (capture → peer → signal)
        │   └── useViewer.js  # Viewer WebRTC logic (signal → peer → display)
        │
        └── utils/
            ├── socket.js     # Socket.io singleton
            └── webrtc.js     # RTCPeerConnection factory (ICE config)

How It Works

Host Flow

  1. User clicks "Start Screen Share" → POST /api/rooms generates a unique roomId
  2. Browser calls getDisplayMedia() to capture the screen
  3. Socket connects and emits host:create with the roomId
  4. For each viewer that joins, the host creates an RTCPeerConnection, adds the screen track, creates an offer, and sends it via the signaling server
  5. Once the viewer answers and ICE negotiation completes, video flows P2P

Viewer Flow

  1. Viewer opens /room/:roomId (no ?host=1 param)
  2. Socket connects and emits viewer:join
  3. Server notifies the host → host sends an offer
  4. Viewer creates RTCPeerConnection, sets remote description, creates answer
  5. ICE candidates are exchanged — P2P connection established
  6. ontrack fires → stream is attached to <video> element
  7. Auto-reconnect kicks in if the connection drops

Room Lifecycle

  • Room is created when host calls host:create
  • Room is destroyed when host disconnects (all viewers notified)
  • Multiple viewers are supported simultaneously (each gets its own peer connection)

Browser Support

Browser Screen Capture Viewing
Chrome 72+ ✓ ✓
Edge 79+ ✓ ✓
Firefox 66+ ✓ ✓
Safari 15.4+ ✓ (limited) ✓

Note: getDisplayMedia requires HTTPS in production. Localhost is exempt.


Scaling Notes

This MVP uses in-memory room storage. For horizontal scaling:

  • Replace the rooms Map with Redis (e.g. ioredis) for shared state across instances
  • Add a TURN server (e.g. Twilio TURN, Cloudflare Calls) for clients behind strict NAT/firewalls
  • The signaling server is stateless-friendly once Redis is added — scale with Railway replicas

License

MIT

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages