← thecodex.expert · The Codex Family of Knowledge
The Codex / JavaScript / WebSockets

WebSockets

Real-time bidirectional communication between browser and server — opening connections, sending messages, handling events, and reconnecting.

What it is, in plain English: A WebSocket is a persistent, full-duplex communication channel between a browser and a server. Unlike HTTP (which is request-response), a WebSocket connection stays open — the server can push data to the client at any time without the client asking. This makes WebSockets ideal for chat apps, live dashboards, multiplayer games, collaborative editing, and any application that needs real-time updates.
Where this bites you

new WebSocket(url) returns immediately, but the connection isn't open yet: the handshake still has to happen over the network. Calling send() right away throws InvalidStateError: Still in CONNECTING state. It's an easy mistake when the first message ("join this room") is written straight after the constructor, as in the sending examples above.

const ws = new WebSocket("ws://localhost:8765/ws");
ws.onmessage = (e) => console.log(e.data);

ws.send(JSON.stringify({ type: "join", room: "general" }));   // right after creating it
// InvalidStateError: Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.

Staying connected — real-time data from the server

HTTP works like sending a letter: you send a request, the server replies, done. A WebSocket is like a phone call: the connection stays open, and both sides can talk whenever they want. This is how chat apps, live stock prices, multiplayer games, and collaborative tools work.

Opening a WebSocket connection

// wss:// = secure WebSocket (like https)
// ws://  = insecure (use only in development/localhost)
const ws = new WebSocket("wss://api.example.com/ws");

// Events — what can happen to the connection:
ws.onopen = () => {
  console.log("Connected to server!");
  ws.send("Hello server!");  // send as soon as connected
};

ws.onmessage = (event) => {
  // event.data = the message from the server (string or Blob)
  console.log("Server says:", event.data);
};

ws.onerror = (error) => {
  console.error("Connection error");
};

ws.onclose = (event) => {
  console.log("Disconnected. Code:", event.code);
};

Sending and receiving messages

// Send a plain text message:
ws.send("Hello!");

// Send JSON (the most common pattern):
ws.send(JSON.stringify({
  type:    "chat",
  room:    "general",
  message: "Hi everyone!",
  timestamp: Date.now(),
}));

// Receive and parse JSON:
ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log("Message type:", data.type);

  if (data.type === "chat") {
    displayMessage(data.message, data.from);
  } else if (data.type === "users") {
    updateUserList(data.users);
  }
};

Send only when readyState is OPEN, and queue anything sent earlier so it goes out from onopen. The ChatRoom.send() below checks readyState but then silently drops early messages. A queue keeps them instead:

const ws = new WebSocket("ws://localhost:8765/ws");
const outbox = [];

function send(msg) {
  const data = JSON.stringify(msg);
  if (ws.readyState === WebSocket.OPEN) ws.send(data);
  else outbox.push(data);                          // not open yet: queue it
}

ws.onopen = () => {
  while (outbox.length) ws.send(outbox.shift());   // flush the queue once connected
};
ws.onmessage = (e) => console.log(e.data);

send({ type: "join", room: "general" });
// echo: {"type":"join","room":"general"}     (from a local echo server)

A simple chat example

class ChatRoom {
  constructor(url) {
    this.ws = new WebSocket(url);
    this.setupHandlers();
  }

  setupHandlers() {
    this.ws.onopen    = () => console.log("Joined chat");
    this.ws.onmessage = (e) => this.handleMessage(JSON.parse(e.data));
    this.ws.onclose   = ()  => console.log("Left chat");
  }

  handleMessage(data) {
    switch (data.type) {
      case "message": displayChat(data.user, data.text); break;
      case "join":    showNotification(`${data.user} joined`); break;
      case "leave":   showNotification(`${data.user} left`);   break;
    }
  }

  send(text) {
    if (this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify({ type: "message", text }));
    }
  }

  leave() { this.ws.close(1000, "User left"); }
}
Try It Yourself

Official Sources

The Codex links only to official documentation and the ECMAScript specification. No third-party tutorials.