Guidance for working with Mini Meet, a minimal 1:1 WebRTC video chat application with Express signaling server, browser WebRTC APIs, and mobile-responsive UI with drag/resize overlays.
This skill has been flagged as potentially dangerous. It contains patterns that could compromise your security or manipulate AI behavior.Safety score: 35/100.
KillerSkills scans all public content for safety. Use caution before installing or executing flagged content.
Provides comprehensive guidance for Claude Code when working with Mini Meet, a minimal 1:1 WebRTC video chat application.
This skill equips Claude Code with deep knowledge of the Mini Meet codebase architecture, development workflows, WebRTC signaling patterns, mobile layout handling, and deployment strategies. Use when working on WebRTC video chat features, debugging peer connections, or extending the Mini Meet platform.
Mini Meet is a minimal 1:1 WebRTC video chat application built with Express and vanilla JavaScript. Users create meetings via `/new`, receive unique URLs (`/m/:id`), and share with one peer. The architecture uses Express for HTTP/WebSocket signaling and browser WebRTC APIs for peer-to-peer media streaming.
**Start development server:**
```bash
npm run dev
```
This runs `scripts/dev.js` which:
**Individual processes:**
```bash
npm run watch-server # Server only with --watch flag
npm run watch-build # Tailwind CSS only
npm run build # One-time Tailwind CSS build
```
**Production:**
```bash
npm start # Runs src/server.js directly
```
**Debugging with DEBUG env var:**
```bash
DEBUG=mini-meet:* # All logs
DEBUG=mini-meet:ws,mini-meet:beacon # Only WebSocket + client beacons
DEBUG=mini-meet:ws:msg # Verbose WebSocket messages
```
Single-file Express server with four primary components:
1. **HTTP/HTTPS Server**: Conditionally creates HTTP or HTTPS based on `SSL_CERT_PATH` and `SSL_KEY_PATH` environment variables
2. **Middleware Stack** (order is critical):
- Cache-Control headers (no caching)
- `express.json()` body parser (must precede `/log` endpoint)
- Request logger with scoped debug loggers attached to `req.log`
- Static file serving from `public/`
- PostHog proxy (`/ph/static/*` MUST come before `/ph/*`)
- Routes: `/new`, `/m/:id`, `/`, `/turn`, `/log`
- Error handling + Rollbar integration
3. **WebSocket Signaling Server**:
- Mounted at `/ws?roomId=xyz`
- In-memory room registry: `Map<roomId, Set<WebSocket>>`
- Enforces 1:1 connection limit (rejects 3rd connection with `room_full`)
- First peer automatically becomes initiator
- Forwards signaling messages: `ready`, `offer`, `answer`, `candidate`, `bye`, `leave`
4. **Scoped Logger Pattern**:
```javascript
req.log = {
http: createLogger('mini-meet:http', { ip, roomId }),
turn: createLogger('mini-meet:turn', { ip }),
beacon: createLogger('mini-meet:beacon', { ip })
};
```
All logs auto-include `ip=X.X.X.X roomId=xyz` context for easy filtering.
Single-page WebRTC client with five core responsibilities:
1. **Media Management**:
- `startLocalMedia()`: Requests camera/mic, displays in all `[data-local-video]` elements
- Track enable/disable for mute/video toggles
- Camera switching on mobile via `swapCameraFacing()`
2. **WebSocket Signaling**:
- `connectWebSocket()`: Connects to `/ws?roomId=xyz`
- Handles: `welcome`, `ready`, `offer`, `answer`, `candidate`, `bye`, `leave`, `room_full`
- Auto-reconnects on disconnect (unless `isShuttingDown` flag is set)
3. **WebRTC Peer Connection**:
- `setupPeerConnection(reason)`: Creates new RTCPeerConnection
- Always tears down old connection before creating new one
- Smart reconnection logic:
- `welcome`: Only recreate if peer connection doesn't exist or is unhealthy
- `bye`: Only tear down if peer connection is actually dead (preserves healthy connections)
- `leave`: Always tear down (explicit peer exit)
- Russian user detection: Skips Google STUN, filters TURN URLs to TCP-only
4. **UI State Management**:
- Mobile: Detects orientation changes, switches between portrait/landscape layouts
- Desktop: Toggle between side-by-side and overlay layouts (persisted in localStorage)
- Mobile overlay: Draggable + pinch-to-resize with touch events
- Status indicator synced between portrait/landscape elements
5. **Client Beacons**:
- `beacon(event, context)`: Sends to `/log` via `navigator.sendBeacon()`
- Forwards to PostHog (if configured) via `window.posthog.capture()`
- Uses Blob with `application/json` type (plain objects send as `text/plain`)
Server-side HTML generation using `nanohtml`:
**Important**: Views use `html` tagged template literals from `nanohtml`. Server-side renders static HTML with embedded JS variables (e.g., `window.__IS_MOBILE__ = ${isMobile}`).
Bypass ad blockers by making PostHog requests appear same-origin.
**Server proxy setup (`src/server.js`)**:
```javascript
// Order matters! Static MUST come before general /ph proxy
app.use('/ph/static', createProxyMiddleware({
target: 'https://eu-assets.i.posthog.com',
changeOrigin: true,
pathRewrite: { '^/': '/static/' }
}));
app.use('/ph', createProxyMiddleware({
target: 'https://eu.i.posthog.com',
changeOrigin: true,
pathRewrite: { '^/ph': '' }
}));
```
**Client setup**:
**Server (`/turn` endpoint)**:
**Client**:
Mobile devices switch between two layouts:
Layout switching happens in `applyMobileLandscapeLayout()` triggered by:
**Mobile overlay**:
**Desktop overlay**:
`detectUnsupportedBrowser()` checks for:
1. Known in-app browsers (Instagram, Facebook, Telegram, etc.)
2. Missing `navigator.mediaDevices.getUserMedia`
If detected, shows modal with "Share Link" button (uses `navigator.share` if available).
Uses `pagehide` event (not `beforeunload`) for iOS Safari compatibility:
```javascript
window.addEventListener('pagehide', () => {
markShuttingDown();
try { send('bye'); } catch (_) {}
});
```
**Required for HTTPS (dev/production)**:
**Optional TURN**:
**Optional Analytics**:
**Server**:
Use scoped loggers attached to `req` or create via `createLogger()`:
```javascript
req.log.http('some message'); // Auto-includes ip and roomId
const wsLogger = createLogger('mini-meet:ws', { ip, roomId });
wsLogger('connection established');
```
Add beacon call and PostHog will auto-receive it:
```javascript
beacon('event_name', { key: 'value' });
// Sends to /log + PostHog (if configured)
```
**Critical**: When editing `welcome` and `bye` handlers, preserve current logic:
Views use `nanohtml` tagged template literals. To add server-side variables:
```javascript
html`<script>window.__MY_VAR__ = ${myVar};</script>`
```
Access in client JS:
```javascript
const myVar = window.__MY_VAR__;
```
**Dokku (recommended)**: See README.md "Deploy on Dokku" section
**Docker**: Use provided `Dockerfile`. Set env vars via docker-compose or runtime flags
**TURN server**: Self-host with coturn (see `coturn/docker-compose.yml`) or use managed provider (Twilio, Cloudflare Calls)
When working with this codebase:
1. **Respect the signaling flow**: WebSocket messages have precise timing and order requirements for WebRTC handshake
2. **Preserve smart reconnection logic**: The `welcome`/`bye` handlers prevent unnecessary peer connection recreation
3. **Maintain middleware order**: PostHog proxy routes and body parser placement are critical
4. **Test mobile layouts**: Changes to overlay or layout logic require testing both portrait and landscape orientations
5. **Use scoped loggers**: Always attach context (ip, roomId) to debug logs for production debugging
6. **Follow beacon patterns**: New analytics events should use the `beacon()` helper for consistent tracking
7. **Check browser compatibility**: When adding media APIs, verify support across iOS Safari, Chrome Mobile, and desktop browsers
8. **Preserve Russian user workarounds**: TURN/STUN filtering for Russian users is a deliberate feature
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/mini-meet-webrtc-project-context/raw