How to Build a WebRTC Voice Call App with PeerJS: Step‑by‑Step Guide
This guide walks through setting up a PeerJS‑based P2P voice call system, covering browser compatibility, Node environment preparation, installing and running the peerjs‑server (via npm or Docker), initializing the Peer object, handling connections, making and receiving calls, and customizing Peer IDs, with full code snippets and screenshots.
Introduction
Recently a project required a web‑based voice call feature. This article shares the deployment process and code for using the open‑source PeerJS P2P voice service.
What is PeerJS?
PeerJS is a JavaScript library built on WebRTC that simplifies peer‑to‑peer communication in browsers, abstracting the complex WebRTC API and enabling easy audio or text data transfer without a middle server.
Environment Preparation
Browser Support
PeerJS works only on browsers that support WebRTC (as shown in the accompanying screenshot).
Node Environment
The tutorial uses Node v12.22.9 and npm 8.5.1; other versions may be incompatible.
peerjs‑server Service
Install the server globally: $ npm install peer -g Start the server: peerjs --port 9000 --key peerjs --path /myapp Or run it with Docker:
$ docker run -p 9000:9000 -d peerjs/peerjs-serverVisit the server URL to verify deployment (screenshot below).
Code Steps
Initialize Peer Object
Define the server URL and create a new Peer instance with appropriate host, port, path, and security settings.
const PEER_SERVER = "https://peer.nodcat.com";
const url = new URL(PEER_SERVER);
const peer = new Peer({
host: url.hostname,
port: url.port || (url.protocol === "https:" ? 443 : 80),
path: url.pathname || "/peerjs",
secure: url.protocol === "https:",
debug: 3 // enable detailed logs
});Listen for Connection
When the peer opens, log the generated ID.
peer.on("open", (id) => {
console.log("My peer ID is: " + id);
myPeerIdEl.textContent = id;
connectionStateEl.textContent = "状态: 已连接";
});Make a Call
Obtain the local audio stream and call the remote peer ID.
stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
mediaConnection = peer.call(partnerId, stream, {
metadata: { username }
});Receive a Call
Handle incoming call events, display caller information, and answer with local media.
peer.on("call", (call) => {
console.log("收到来电", call);
incomingCallConnection = call;
if (call.metadata && call.metadata.username) {
partnerName = call.metadata.username;
incomingCallerNameEl.textContent = partnerName;
} else {
incomingCallerNameEl.textContent = "未知来电";
}
// show incoming UI
connectScreen.classList.add("hidden");
incomingCallScreen.classList.remove("hidden");
acceptCallBtn.onclick = async () => {
try {
stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
initAudioAnalyzer(stream);
call.answer(stream);
handleCall(call);
incomingCallScreen.classList.add("hidden");
callerNameEl.textContent = partnerName;
callScreen.classList.remove("hidden");
} catch (err) {
console.error("无法获取媒体设备:", err);
alert("无法访问麦克风,请确保已授予权限");
incomingCallScreen.classList.add("hidden");
connectScreen.classList.remove("hidden");
}
};
});Handle Audio Stream
When the remote stream arrives, attach it to an audio element and manage autoplay restrictions.
call.on("stream", (remoteStream) => {
console.log("收到远程流,轨道:", remoteStream.getTracks());
audioStateEl.textContent = "已接收音频";
remoteAudio.srcObject = remoteStream;
const playPromise = remoteAudio.play();
if (playPromise !== undefined) {
playPromise.catch(error => {
console.log("自动播放被阻止:", error);
callStatusEl.textContent = "点击屏幕以启用声音";
document.addEventListener("click", enableAudio, { once: true });
});
}
});Custom Peer ID
Peer IDs can be set manually by passing a string to the Peer constructor, allowing backend systems to bind users to specific IDs.
const peer = new Peer("custom-peer-id", {
host: url.hostname,
port: url.port || (url.protocol === "https:" ? 443 : 80),
path: url.pathname || "/peerjs",
secure: url.protocol === "https:",
debug: 3
});Demo Screenshots
Nickname registration page, connection page, and call page are shown below.
Conclusion
PeerJS offers a simple, extensible P2P voice and text server/client that can be deployed with minimal code, providing a solid foundation for adding multi‑party calls, video conferencing, and other real‑time communication features.
Project repository: https://github.com/peers
Dunmao Tech Hub
Sharing selected technical articles synced from CSDN. Follow us on CSDN: Dunmao.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
