Skip to content

Instantly share code, notes, and snippets.

@wchargin
Last active February 5, 2025 00:16
Show Gist options
  • Save wchargin/1ec26297b2a41995eed14628383d39f7 to your computer and use it in GitHub Desktop.
Save wchargin/1ec26297b2a41995eed14628383d39f7 to your computer and use it in GitHub Desktop.
<!doctype html>
<style>
#form {
display: flex;
gap: 10px;
}
p {
margin: 0.5em 0;
}
</style>
<div id="form">
<label>
<input type="checkbox" id="ckbxChannel" checked /> Offer channel?
</label>
<button id="go">Open connection</button>
</div>
<div id="sink"></div>
<script>
async function main(options) {
const newPeer = () => {
const config = {}; // can put ICE servers here if wanted
return new RTCPeerConnection(config);
};
const alice = newPeer();
const bob = newPeer();
function log(text) {
const el = document.createElement("p");
el.innerText = text;
sink.appendChild(el);
}
function listen(targetName, target, eventName) {
target.addEventListener(eventName, (event) => {
const fields = Object.fromEntries(
["connectionState", "signalingState", "iceGatheringState"].map(
(k) => [k, event.target.connectionState],
),
);
let parts = [targetName, eventName, JSON.stringify(fields)];
if (event.channel) {
parts.push("label=" + JSON.stringify(event.channel.label));
}
log(parts.join(" "));
});
}
for (const [name, peer] of Object.entries({ alice, bob })) {
listen(name, peer, "connectionstatechange");
listen(name, peer, "datachannel");
listen(name, peer, "icecandidate");
listen(name, peer, "icecandidateerror");
listen(name, peer, "iceconnectionstatechange");
listen(name, peer, "icegatheringstatechange");
listen(name, peer, "signalingstatechange");
listen(name, peer, "track");
}
alice.addEventListener("icecandidate", (event) => {
bob.addIceCandidate(event.candidate);
});
bob.addEventListener("icecandidate", (event) => {
alice.addIceCandidate(event.candidate);
});
let ch1;
if (options?.offerChannel) {
ch1 = alice.createDataChannel("in-band, pre-offer", { id: 7 });
}
bob.addEventListener("datachannel", (event) => {
const { channel } = event;
channel.addEventListener("message", (message) => {
const context = `bob.channels[${channel.label}]`;
log(`${context} message: ${message.data}`);
});
});
const offerConfig = {
// Need to have either opened a data channel already, or else provide one
// of `offerToReceiveAudio` or `offerToReceiveVideo`; otherwise, nothing
// will happen at all.
offerToReceiveAudio: 1,
};
const offer = await alice.createOffer(offerConfig);
await alice.setLocalDescription(offer);
await bob.setRemoteDescription(offer);
console.log({ offer });
console.log(offer.sdp);
const answer = await bob.createAnswer();
await bob.setLocalDescription(answer);
await alice.setRemoteDescription(answer);
console.log({ answer });
Object.assign(globalThis, { alice, bob, offer, answer, ch1 });
}
go.addEventListener("click", () => {
sink.innerHTML = ""; // clear
const offerChannel = ckbxChannel.checked;
main({ offerChannel });
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment