Concepts 🚀 Project 🧮 Labs
📚 Learn / Mini Projects / WebSocket Chat Room

WebSocket Chat Room

Real-time chat — messages broadcast to every connected browser instantly. FastAPI WS + vanilla JS client.

🚀 Mini Project · 3 files

Project files

📄 main.py python
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.responses import HTMLResponse
import uvicorn

app = FastAPI()

# Track all connected clients
clients: set[WebSocket] = set()

@app.get('/')
async def get():
    return HTMLResponse(open('index.html').read())

@app.websocket('/ws')
async def ws_endpoint(ws: WebSocket):
    await ws.accept()
    clients.add(ws)
    try:
        while True:
            msg = await ws.receive_text()
            # Broadcast to every connected client
            for c in list(clients):
                await c.send_text(msg)
    except WebSocketDisconnect:
        clients.remove(ws)

if __name__ == '__main__':
    uvicorn.run(app, host='0.0.0.0', port=8000)
📄 index.html html
<!DOCTYPE html>
<html>
<head><title>Chat</title>
<style>
  #msgs { height: 300px; overflow-y: scroll; border: 1px solid #ccc;
          padding: 0.5rem; margin-bottom: 0.5rem; }
  .msg { margin: 0.25rem 0; }
  input { padding: 0.5rem; }
</style></head>
<body>
  <div id='msgs'></div>
  <input id='input' placeholder='type a message' size='50'>

  <script>
    const ws = new WebSocket('ws://' + location.host + '/ws');
    const msgs = document.getElementById('msgs');
    const input = document.getElementById('input');

    ws.onmessage = (e) => {
      const div = document.createElement('div');
      div.className = 'msg';
      div.textContent = e.data;
      msgs.appendChild(div);
      msgs.scrollTop = msgs.scrollHeight;  // auto-scroll
    };

    input.addEventListener('keydown', (e) => {
      if (e.key === 'Enter' && input.value.trim()) {
        ws.send(input.value);
        input.value = '';
      }
    });

    ws.onclose = () => {
      setTimeout(() => location.reload(), 2000);  // reconnect
    };
  </script>
</body>
</html>
📄 run.sh bash
pip install fastapi uvicorn
python main.py

# Open http://localhost:8000 in TWO browser tabs
# Type a message in either tab — it appears in both instantly

How it works

WebSockets give you a persistent bidirectional connection — either side can send at any time, unlike HTTP where the client must ask. The chat flow: (1) Browser opens ws://localhost:8000/ws. The server accepts and adds the WebSocket to a set of connected clients. (2) User types a message and hits Enter. ws.send(input.value) sends it to the server. (3) The server receives it via ws.receive_text(), then loops over every connected client and sends the same message. This is the 'broadcast' pattern — one message goes to everyone. (4) Each browser's ws.onmessage fires, appends the message to the DOM, and auto-scrolls to the bottom. (5) On disconnect (user closes tab), the WebSocketDisconnect exception fires, the server removes the client from the set. (6) ws.onclose on the client triggers a reload after 2s — basic auto-reconnect. Open two browser tabs to see real-time sync.

⚡ Open in Notebook
🔍 Where this is used in OpenBenchML OpenBenchML's realtime.html + app/main.py use this exact pattern for the live leaderboard — when a benchmark finishes, the result is broadcast to every connected browser.