Blog

Create Peer-to-Peer Web Apps in seconds with Puter.js

On this page

WebRTC is one of the best ways to build real-time peer-to-peer experiences in the browser. However, it is difficult to set up, requiring the management of TURN and STUN connections, an external server for signalling, and hundreds of lines of boilerplate and config.

The new Peer API lets you drag and drop real time communication directly your existing webapp, giving you WebRTC data channels with built-in signaling and Puter-managed TURN relays. This makes it a perfect fit for chat, multiplayer games, collaborative tools, and any app that needs low-latency browser-to-browser communication.

Start a peer server

In Puter.js, peer connections are represented by a server/client model. Many users can connect to one user who's running a server, and the server will manage connections.

Starting a peer server is a single call. Puter returns an invite code you can share with another client. Only users who you've given the code to will be able to connect.

<script src="https://js.puter.com/v2/"></script>
<script>
    async function startServer() {
        const server = await puter.peer.serve();
        puter.print(`Invite code: ${server.inviteCode}`);

        server.addEventListener("connection", (event) => {
            const conn = event.conn;
            const user = event.user;
            puter.print(`${user.username} just connected to our server!`);

            conn.addEventListener("open", () => {
                conn.send("Hello from the server!");
            });

            conn.addEventListener("message", (msg) => {
                puter.print(`Client says: ${msg.data}`);
            });
        });
    }
</script>

Connect from another client

On the client side, simply connect with the invite code:

<script src="https://js.puter.com/v2/"></script>
<script>
    async function connect(inviteCode) {
        const conn = await puter.peer.connect(inviteCode);

        conn.addEventListener("open", () => {
            conn.send("Hello from the client!");
        });

        conn.addEventListener("message", (msg) => {
            puter.print(`Server says: ${msg.data}`);
        });

        conn.addEventListener("close", (event) => {
            puter.print(`Connection closed: ${event.reason || "no reason given"}`);
        });
    }
</script>

And just like that, you'll be able to send whatever data you want between the two devices. On top of these simple data channels, you can build anything: multiplayer games, live chat, and more.

A complete chat example

Here is a minimal peer-to-peer chat app using the serve() and connect() APIs:

<!doctype html>
<html>
<body>
    <script src="https://js.puter.com/v2/"></script>

    <h3>Peer Chat</h3>
    <p>Open this page in two tabs. Start a server in one tab, then connect from the other.</p>

    <div style="margin-bottom: 10px;">
        <button id="start-server">Start server</button>
        <span id="invite" style="margin-left: 10px;"></span>
    </div>

    <div style="margin-bottom: 10px;">
        <input id="invite-input" placeholder="Invite code" style="width: 220px;" />
Show 94 more lines...
<!doctype html>
<html>
<body>
    <script src="https://js.puter.com/v2/"></script>

    <h3>Peer Chat</h3>
    <p>Open this page in two tabs. Start a server in one tab, then connect from the other.</p>

    <div style="margin-bottom: 10px;">
        <button id="start-server">Start server</button>
        <span id="invite" style="margin-left: 10px;"></span>
    </div>

    <div style="margin-bottom: 10px;">
        <input id="invite-input" placeholder="Invite code" style="width: 220px;" />
        <button id="connect">Connect</button>
    </div>

    <div style="margin-bottom: 10px;">
        <input id="message" placeholder="Message" style="width: 220px;" />
        <button id="send" disabled>Send</button>
    </div>

    <pre id="log" style="background: #f4f4f4; padding: 10px; height: 200px; overflow: auto;"></pre>

    <script>
        const logEl = document.getElementById("log");
        const inviteEl = document.getElementById("invite");
        const inviteInput = document.getElementById("invite-input");
        const messageInput = document.getElementById("message");
        const sendBtn = document.getElementById("send");

        let activeConn = null;

        function log(...args) {
            logEl.textContent += `${args.join(" ")}\n`;
            logEl.scrollTop = logEl.scrollHeight;
        }

        function bindConnection(conn, role) {
            activeConn = conn;
            sendBtn.disabled = true;

            conn.addEventListener("open", () => {
                log(`[${role}] connected`);
                sendBtn.disabled = false;
            });

            conn.addEventListener("message", (event) => {
                log(`[${role}] received:`, event.data);
            });

            conn.addEventListener("close", (event) => {
                log(`[${role}] closed`, event.reason ? `(${event.reason})` : "");
                sendBtn.disabled = true;
            });

            conn.addEventListener("error", (event) => {
                log(`[${role}] error`, event.error?.message || event.error || "unknown error");
            });
        }

        document.getElementById("start-server").addEventListener("click", async () => {
            inviteEl.textContent = "Starting...";

            try {
                const server = await puter.peer.serve();
                inviteEl.textContent = `Invite code: ${server.inviteCode}`;
                log("[server] ready, waiting for connection");

                server.addEventListener("connection", (event) => {
                    log("[server] client connected");
                    bindConnection(event.conn, "server");
                });
            } catch (err) {
                inviteEl.textContent = "Failed to start server.";
                log("[server] error", err?.message || err);
            }
        });

        document.getElementById("connect").addEventListener("click", async () => {
            const inviteCode = inviteInput.value.trim();

            if (!inviteCode) {
                log("[client] enter an invite code first");
                return;
            }

            try {
                const conn = await puter.peer.connect(inviteCode);
                log("[client] connecting...");
                bindConnection(conn, "client");
            } catch (err) {
                log("[client] error", err?.message || err);
            }
        });

        sendBtn.addEventListener("click", () => {
            const message = messageInput.value.trim();

            if (!message || !activeConn) return;

            activeConn.send(message);
            log("[you] sent:", message);
            messageInput.value = "";
        });
    </script>
</body>
</html>
Collapse code

Get started

If you want to get started with peer to peer, just drop the Puter.js script tag into your app. On top of the easy WebRTC setup, you also get immediate access to easy authentication, cloud storage, AI services, and much more. You can about what else you can do with Puter.js here.

We're excited to see what you build! From multiplayer browser games to collaborative editors and real-time dashboards, we want to see what you can accomplish with Puter.js

Free, Serverless AI and Cloud

Start creating powerful web applications with Puter.js in seconds!

Get Started Now

Read the Docs Try the Playground