Skip to content

Instantly share code, notes, and snippets.

@TimelessP
Last active November 10, 2024 09:49
Show Gist options
  • Save TimelessP/5693c9a18be714c849e450c038b0ed54 to your computer and use it in GitHub Desktop.
Save TimelessP/5693c9a18be714c849e450c038b0ed54 to your computer and use it in GitHub Desktop.
A lightweight web app that enables peer-to-peer messaging over a local network without requiring an internet connection or a dedicated server. Utilizing WebRTC, the app ensures secure, direct communication between devices on the same LAN. Perfect for offline environments, this app allows users to exchange text messages effortlessly by manually s…
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LAN Peer Communication</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f9f9f9;
}
h1 {
text-align: center;
}
textarea, input, button {
width: calc(100% - 110px);
box-sizing: border-box;
margin-bottom: 10px;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
font-size: 16px;
display: inline-block;
}
button {
width: 100px;
cursor: pointer;
background-color: #007BFF;
color: white;
border: none;
padding: 10px;
margin-left: 10px;
font-size: 16px;
border-radius: 5px;
}
button:hover {
background-color: #0056b3;
}
#receivedMessages {
background-color: #fff;
border: 1px solid #ccc;
border-radius: 5px;
padding: 10px;
height: 150px;
overflow-y: auto;
}
.collapsible {
background-color: #007BFF;
color: white;
cursor: pointer;
padding: 10px;
border: none;
border-radius: 5px;
width: 100%;
text-align: left;
outline: none;
}
.collapsible:hover {
background-color: #0056b3;
}
.content {
padding: 10px;
display: none;
background-color: #f1f1f1;
margin-top: 5px;
border: 1px solid #ccc;
border-radius: 5px;
}
</style>
</head>
<body>
<h1>LAN Peer Communication</h1>
<button class="collapsible">Instructions</button>
<div class="content">
<p>1. Click "Create Offer" on one computer and copy the signaling data.</p>
<p>2. Paste the signaling data into the "Remote Signaling Data" field on the second computer, then click "Create Answer."</p>
<p>3. Copy the answer and paste it back into the "Remote Signaling Data" field on the first computer, then click "Set Remote Description."</p>
<p>4. Once connected, use the input field to send messages!</p>
</div>
<h3>Your Signaling Data</h3>
<textarea id="localSignalingData" readonly placeholder="Your signaling data will appear here"></textarea>
<button id="copyLocal">Copy</button>
<h3>Paste Remote Signaling Data</h3>
<textarea id="remoteSignalingData" placeholder="Paste the remote signaling data here"></textarea>
<button id="copyRemote">Copy</button>
<button id="pasteRemote" style="display: none;">Paste</button>
<button id="createOffer">Create Offer</button>
<button id="createAnswer">Create Answer</button>
<button id="setRemote">Set Remote Description</button>
<h3>Send a Message</h3>
<input type="text" id="message" placeholder="Type a message">
<button id="sendMessage">Send Message</button>
<h3>Received Messages</h3>
<div id="receivedMessages"></div>
<p>
<a href="https://gist.github.com/TimelessP/5693c9a18be714c849e450c038b0ed54">https://gist.github.com/TimelessP/5693c9a18be714c849e450c038b0ed54</a>
</p>
<script>
const peerConnection = new RTCPeerConnection();
let dataChannel;
// Automatically handle ICE candidates
peerConnection.onicecandidate = (event) => {
if (event.candidate) {
document.getElementById("localSignalingData").value = JSON.stringify(peerConnection.localDescription);
}
};
// Handle received data channel
peerConnection.ondatachannel = (event) => {
dataChannel = event.channel;
setupDataChannel();
};
// Create a data channel for the offerer
document.getElementById("createOffer").addEventListener("click", async () => {
dataChannel = peerConnection.createDataChannel("chat");
setupDataChannel();
const offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);
document.getElementById("localSignalingData").value = JSON.stringify(peerConnection.localDescription);
});
// Create an answer for the remote offer
document.getElementById("createAnswer").addEventListener("click", async () => {
const remoteData = document.getElementById("remoteSignalingData").value;
if (!remoteData) return alert("Paste the remote offer first!");
const offer = JSON.parse(remoteData);
await peerConnection.setRemoteDescription(offer);
const answer = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(answer);
document.getElementById("localSignalingData").value = JSON.stringify(peerConnection.localDescription);
});
// Set remote description for the answer
document.getElementById("setRemote").addEventListener("click", async () => {
const remoteData = document.getElementById("remoteSignalingData").value;
if (!remoteData) return alert("Paste the remote answer first!");
const answer = JSON.parse(remoteData);
await peerConnection.setRemoteDescription(answer);
});
// Send a message through the data channel
document.getElementById("sendMessage").addEventListener("click", () => {
const message = document.getElementById("message").value;
if (dataChannel && dataChannel.readyState === "open") {
dataChannel.send(message);
const receivedMessages = document.getElementById("receivedMessages");
receivedMessages.innerHTML += `<p><strong>Sent:</strong> ${message}</p>`;
} else {
alert("Data channel is not open!");
}
});
// Copy buttons functionality
document.getElementById("copyLocal").addEventListener("click", () => {
const text = document.getElementById("localSignalingData").value;
navigator.clipboard.writeText(text).then(() => alert("Copied!"));
});
document.getElementById("copyRemote").addEventListener("click", () => {
const text = document.getElementById("remoteSignalingData").value;
navigator.clipboard.writeText(text).then(() => alert("Copied!"));
});
// Paste button functionality (if supported)
if (navigator.clipboard && navigator.clipboard.readText) {
document.getElementById("pasteRemote").style.display = "inline-block";
document.getElementById("pasteRemote").addEventListener("click", async () => {
const text = await navigator.clipboard.readText();
document.getElementById("remoteSignalingData").value = text;
});
}
// Collapsible instructions
document.querySelector(".collapsible").addEventListener("click", function () {
this.classList.toggle("active");
const content = this.nextElementSibling;
content.style.display = content.style.display === "block" ? "none" : "block";
});
// Setup the data channel events
function setupDataChannel() {
dataChannel.onopen = () => console.log("Data channel is open");
dataChannel.onmessage = (event) => {
const receivedMessages = document.getElementById("receivedMessages");
receivedMessages.innerHTML += `<p><strong>Received:</strong> ${event.data}</p>`;
};
dataChannel.onclose = () => console.log("Data channel is closed");
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment