Skip to content
Back to skills

Webrtc

ASecurity

WebRTC peer-to-peer communication. Signaling, ICE/STUN/TURN, media streams, data channels, screen sharing, and SFU integration (mediasoup, LiveKit). USE WHEN: user mentions "WebRTC", "video call", "peer-to-peer", "P2P", "screen sharing", "data channel", "STUN", "TURN", "mediasoup", "LiveKit" DO NOT USE FOR: server-to-client streaming - use `sse`; chat messaging - use `socket-io`

  • 31 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 8, 2026
ai-agentstypescriptgo

Works with

  • cursor
  • cli

Security analysis

A100/100

Scanned September 8, 2026

npx -y skills add claude-dev-suite/claude-dev-suite --skill webrtc --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Webrtc?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Webrtc
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/claude-dev-suite-webrtc/badge)](https://www.skillsdirectory.com/skills/claude-dev-suite-webrtc)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: webrtc
description: |
  WebRTC peer-to-peer communication. Signaling, ICE/STUN/TURN, media streams,
  data channels, screen sharing, and SFU integration (mediasoup, LiveKit).

  USE WHEN: user mentions "WebRTC", "video call", "peer-to-peer", "P2P",
  "screen sharing", "data channel", "STUN", "TURN", "mediasoup", "LiveKit"

  DO NOT USE FOR: server-to-client streaming - use `sse`;
  chat messaging - use `socket-io`
allowed-tools: Read, Grep, Glob, Write, Edit
---
# WebRTC

## Peer Connection Setup

```typescript
const config: RTCConfiguration = {
  iceServers: [
    { urls: 'stun:stun.l.google.com:19302' },
    { urls: 'turn:turn.example.com', username: 'user', credential: 'pass' },
  ],
};

const pc = new RTCPeerConnection(config);

// Get local media
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
stream.getTracks().forEach((track) => pc.addTrack(track, stream));

// Display remote stream
pc.ontrack = (event) => {
  remoteVideo.srcObject = event.streams[0];
};

// ICE candidates — send to remote peer via signaling
pc.onicecandidate = (event) => {
  if (event.candidate) {
    signalingChannel.send({ type: 'ice-candidate', candidate: event.candidate });
  }
};
```

## Signaling (via Socket.IO)

```typescript
// Caller
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
socket.emit('signal', { type: 'offer', sdp: offer });

// Callee — on receiving offer
socket.on('signal', async (msg) => {
  if (msg.type === 'offer') {
    await pc.setRemoteDescription(msg.sdp);
    const answer = await pc.createAnswer();
    await pc.setLocalDescription(answer);
    socket.emit('signal', { type: 'answer', sdp: answer });
  } else if (msg.type === 'answer') {
    await pc.setRemoteDescription(msg.sdp);
  } else if (msg.type === 'ice-candidate') {
    await pc.addIceCandidate(msg.candidate);
  }
});
```

## Data Channels

```typescript
const dataChannel = pc.createDataChannel('chat', { ordered: true });

dataChannel.onopen = () => dataChannel.send('Hello!');
dataChannel.onmessage = (e) => console.log('Received:', e.data);

// Remote side
pc.ondatachannel = (event) => {
  const channel = event.channel;
  channel.onmessage = (e) => console.log('Received:', e.data);
};
```

## Screen Sharing

```typescript
const screenStream = await navigator.mediaDevices.getDisplayMedia({
  video: { cursor: 'always' },
  audio: true,
});

const videoTrack = screenStream.getVideoTracks()[0];
const sender = pc.getSenders().find((s) => s.track?.kind === 'video');
await sender?.replaceTrack(videoTrack);

videoTrack.onended = () => {
  // User stopped sharing — switch back to camera
  const cameraTrack = localStream.getVideoTracks()[0];
  sender?.replaceTrack(cameraTrack);
};
```

## LiveKit (SFU — recommended for group calls)

```typescript
import { Room, RoomEvent } from 'livekit-client';

const room = new Room();
await room.connect(LIVEKIT_URL, accessToken);

await room.localParticipant.enableCameraAndMicrophone();

room.on(RoomEvent.TrackSubscribed, (track, publication, participant) => {
  const element = track.attach();
  document.getElementById('remote-videos')!.appendChild(element);
});

room.on(RoomEvent.ParticipantDisconnected, (participant) => {
  console.log(`${participant.identity} left`);
});
```

## Anti-Patterns

| Anti-Pattern | Fix |
|--------------|-----|
| No TURN server | Always configure TURN for NAT traversal |
| P2P for group calls (>4 users) | Use SFU (LiveKit, mediasoup) |
| No error handling on getUserMedia | Handle NotAllowedError, NotFoundError |
| Signaling over unencrypted channel | Use WSS (WebSocket Secure) |
| No connection state monitoring | Listen to `connectionstatechange` event |

## Production Checklist

- [ ] TURN server deployed and configured
- [ ] SFU for group video (>3 participants)
- [ ] Fallback UI when media access denied
- [ ] Connection quality monitoring
- [ ] Bandwidth adaptation (simulcast)
- [ ] Recording capability if needed

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…