Skip to content

Instantly share code, notes, and snippets.

@derekmcloughlin
Created July 11, 2025 14:48
Show Gist options
  • Select an option

  • Save derekmcloughlin/f6bc62ab8d30476c5e5767eb5cb3c161 to your computer and use it in GitHub Desktop.

Select an option

Save derekmcloughlin/f6bc62ab8d30476c5e5767eb5cb3c161 to your computer and use it in GitHub Desktop.
WebRTC Demo
<!DOCTYPE html>
<html>
<body>
<h3>Local SDP (copy this to remote)</h3>
<textarea id="local" style="width:100%;height:150px"></textarea>
<h3>Remote SDP (paste from other peer)</h3>
<textarea id="remote" style="width:100%;height:150px"></textarea>
<br />
<button onclick="createOffer()">Create Offer</button>
<button onclick="createAnswer()">Create Answer</button>
<button onclick="connect()">Set Remote Description</button>
<br /><br />
<div>
<input type="text" id="messageInput" placeholder="Type a message...">
<button onclick="sendMessage()">Send Message</button>
</div>
<div id="messages"></div>
<script>
const pc = new RTCPeerConnection({
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' },
{ urls: 'stun:stun1.l.google.com:19302' }
]
});
let dataChannel = null;
let isInitiator = false;
// Handle ICE candidates
pc.onicecandidate = (e) => {
if (e.candidate === null) {
// ICE gathering complete, display the SDP
document.getElementById('local').value = JSON.stringify(pc.localDescription);
}
};
// Handle incoming data channel (for answerer)
pc.ondatachannel = (event) => {
const receiveChannel = event.channel;
setupDataChannel(receiveChannel);
};
// Handle connection state changes
pc.onconnectionstatechange = () => {
console.log('Connection state:', pc.connectionState);
if (pc.connectionState === 'connected') {
addMessage('Connected to peer!');
}
};
function setupDataChannel(channel) {
dataChannel = channel;
channel.onopen = () => {
console.log('Data channel opened');
addMessage('Data channel opened');
if (isInitiator) {
channel.send("Hello from initiator!");
} else {
channel.send("Hello from answerer!");
}
};
channel.onmessage = (e) => {
console.log("Received:", e.data);
addMessage(`Received: ${e.data}`);
};
channel.onclose = () => {
console.log('Data channel closed');
addMessage('Data channel closed');
};
}
window.createOffer = async () => {
isInitiator = true;
// Create data channel first for initiator
const channel = pc.createDataChannel("chat");
setupDataChannel(channel);
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
addMessage('Offer created. Copy the Local SDP to the other peer.');
};
window.createAnswer = async () => {
isInitiator = false;
const remoteSDP = JSON.parse(document.getElementById('remote').value);
await pc.setRemoteDescription(new RTCSessionDescription(remoteSDP));
const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
addMessage('Answer created. Copy the Local SDP back to the initiator.');
};
window.connect = async () => {
const remoteSDP = JSON.parse(document.getElementById('remote').value);
await pc.setRemoteDescription(new RTCSessionDescription(remoteSDP));
addMessage('Remote description set. Connection should establish shortly.');
};
window.sendMessage = () => {
const input = document.getElementById('messageInput');
const message = input.value.trim();
if (message && dataChannel && dataChannel.readyState === 'open') {
dataChannel.send(message);
addMessage(`Sent: ${message}`);
input.value = '';
} else {
addMessage('Data channel not ready or message empty');
}
};
function addMessage(message) {
const messagesDiv = document.getElementById('messages');
const messageElement = document.createElement('div');
messageElement.textContent = `${new Date().toLocaleTimeString()}: ${message}`;
messagesDiv.appendChild(messageElement);
messagesDiv.scrollTop = messagesDiv.scrollHeight;
}
// Allow Enter key to send message
document.getElementById('messageInput').addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
sendMessage();
}
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment