Chat example in Javascript

The most trivial example. A chat based on wrapping cat(1) without any backend code.

View demo online
View full source on GitHub
Run the example

Running the example

You can run this self-contained example using Docker.

git clone https://github.com/scalesocket/scalesocket
cd scalesocket/examples/
docker compose up --build chat

Then open http://localhost:5000/ in your browser.

Frontend code

The frontend is a single html file, index.html, with some javascript. It connects with websockets to the server and shows a chat interface.

<!doctype html>
<html>

<head>
    <title>Chat Example using ScaleSocket</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        body { background-color: #e2e8f0; font-family: Arial, Helvetica, sans-serif; margin: 0; text-align: center; }
        #chat { margin: 0 auto; height: 50vh; display: flex; flex-direction: column; text-align: left; max-width: 48rem; }
        #messages { background-color: #f1f5f9; overflow-x: scroll; padding: 1rem; }
        #message { display: flex; flex-direction: row; }
        .input-rounded { display: flex; border: 0; padding: 0.25rem; margin: 0.5rem; border-radius: 0.75rem; }
        .grow { flex-grow: 1; }
        .shrink { flex-shrink: 1; min-width: 0;  }
    </style>
</head>

<body>
    <div>
        <h1>Multiplayer Chat Example</h1>
        <p><select
                onchange="let value = this.options[this.selectedIndex].value || (Math.random() + 1).toString(36).substring(7); window.location.search = `?room=${value}`;">
                <option disabled selected>Change Room</option>
                <option value="default">Room 0 (default)</option>
                <option value="room1">Room 1</option>
                <option value="room2">Room 2</option>
                <option value="room3">Room 3</option>
                <option value="">Create new room</option>
            </select></p>
        <p>Multiplayer chat, try opening multiple instances. Built with <a
                href="https://www.scalesocket.org/man/examples/chat" target="_blank">ScaleSocket</a>.</p>
    </div>
    <div id="chat">
        <div id="messages" class="grow"></div>
        <div id="message">
            <input name="nick" placeholder="nick" class="input-rounded shrink" value="anonymous" />
            <input name="message" placeholder="connecting..." class="input-rounded grow" disabled />
        </div>
    </div>
    <script>
        const $ = document.querySelector.bind(document);
        const protocol = window.location.protocol === "https:" ? "wss" : "ws";
        const params = new URLSearchParams(document.location.search);
        const room = params.get("room") ?? 'default';

        // connect to websocket server and room based on URL
        const ws = new WebSocket(`${protocol}:${window.location.host}/${room}`);

        const sendMessage = (text) => {
            // send outgoing message
            const nick = $("input[name=nick]").value || "anonymous";
            ws.send(JSON.stringify({ nick, text }));
        }

        const recvMessage = (message) => {
            // handle incoming message
            const el = $("#messages")
            el.appendChild(document.createTextNode(`<${message.nick}> ${message.text}`));
            el.appendChild(document.createElement('br'));
            el.scrollTop = el.scrollHeight;
        }

        // set websocket listener
        ws.addEventListener('message', e => {
            const message = JSON.parse(e.data);
            recvMessage(message);
        });
        // set input listener
        $("input[name=message]").addEventListener('keyup', ({ key, target }) => {
            if (key === 'Enter') {
                sendMessage(target.value);
                target.value = "";
                target.focus();
            }
        });

        ws.addEventListener('open', _ => {
            // set input focus
            $("input[name=message]").placeholder = "message...";
            $("input[name=message]").disabled = false;
            $("input[name=message]").focus();
        });
    </script>
</body>

</html>

Backend code

There is no backend code. The backend is a wrapped cat process.

Backend server

The backend is the ScaleSocket server.

We want to:

  • let participants join rooms based on URL
  • start a new cat process when a new user connects
  • keep a history of 64 messages and send it to joining users
  • host a static html file

To do this, start ScaleSocket using:

scalesocket --addr 0.0.0.0:5000\
    --staticdir /var/www/public/\
    --frame\
    --cache=all:64\
    cat

How does it work?

The frontend connects to the server using websockets. The backend spins up a new cat process.

When the frontend sends a chat message, ScaleSocket passes it directly to the stdin of cat.

Since cat echoes all input it receives, the reply to stdout is the message itself, which ScaleSocket sends back to all connected clients.

This is obviously not a tamper-proof setup. A more complete example would utilize the _from field, which is added by the scalesocket server when --frame=json is specified.

Was this page helpful?