Learn how to create a live, real-time chat application in JavaScript using the WebSocket protocol from scratch, no frameworks required.

Introduction
We live in an era where users expect instant updates, whether it’s new messages, notifications, or multiplayer game moves.
If you’ve ever wondered how chat apps like WhatsApp Web or Slack exchange messages instantly, the answer lies in WebSockets.
WebSockets enable real-time, two-way communication between the browser and the server without the constant overhead of HTTP requests or polling.
In this post, we’ll build a simple but fully functional chat app using pure JavaScript and Node.js, step by step.
No frameworks. No Socket.IO. Just the raw WebSocket API.
Let’s get started.
1. What Are WebSockets?
Before diving into code, let’s quickly understand what WebSockets are.
In traditional HTTP, the communication is one-way:
Client sends a request → Server sends a response → Connection closes.
But in a chat app, both sides need to talk freely.
That’s where WebSockets come in.
They create a persistent connection that allows the client and the server to exchange data in both directions anytime, instantly.
This makes them perfect for:
- Chat applications
- Live dashboards
- Real-time notifications
- Collaborative tools
2. Setting Up the Project
We’ll start with a fresh Node.js project.
mkdir websocket-chat-app
cd websocket-chat-app
npm init -y
Now install the WebSocket package for Node.js:
npm install ws
✅ This library (ws) is lightweight and implements the WebSocket protocol.
Project Structure
websocket-chat-app/
│
├── server.js # Backend WebSocket server
└── public/
└── index.html # Frontend chat UI
3. Building the WebSocket Server
Let’s first create our backend.
Create a new file: server.js
import { WebSocketServer } from 'ws';
import { createServer } from 'http';
import { readFileSync } from 'fs';
import { resolve } from 'path';
// Serve the frontend HTML file
const server = createServer((req, res) => {
if (req.url === '/') {
const html = readFileSync(resolve('public', 'index.html'), 'utf-8');
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(html);
}
});
// Create a WebSocket server instance
const wss = new WebSocketServer({ server });
// Handle new connections
wss.on('connection', (socket) => {
console.log('New user connected');
// Listen for messages from the client
socket.on('message', (message) => {
console.log('Received:', message.toString());
// Broadcast message to all clients
wss.clients.forEach((client) => {
if (client.readyState === 1) {
client.send(message.toString());
}
});
});
socket.on('close', () => console.log('User disconnected'));
});
const PORT = 3000;
server.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`));
✅ This file does three things:
- Serves the frontend
index.html. - Handles WebSocket connections.
- Broadcasts messages to every connected user.
That’s your full backend in less than 40 lines.
4. Creating the Frontend
Now, let’s build the client side.
Inside the public folder, create index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>WebSocket Chat App</title>
<style>
body {
font-family: system-ui, sans-serif;
background: #f4f6f8;
margin: 0;
display: flex;
flex-direction: column;
height: 100vh;
}
header {
background: #007bff;
color: white;
text-align: center;
padding: 12px;
font-size: 1.2em;
}
#chat {
flex: 1;
background: #fff;
overflow-y: auto;
padding: 10px;
border: 1px solid #ddd;
}
form {
display: flex;
background: #fff;
border-top: 1px solid #ddd;
padding: 10px;
}
input {
flex: 1;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 1em;
}
button {
background: #007bff;
color: white;
border: none;
margin-left: 8px;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
}
</style>
</head>
<body>
<header>💬 WebSocket Chat App</header>
<div id="chat"></div>
<form id="form">
<input id="message" placeholder="Type a message..." autocomplete="off" />
<button>Send</button>
</form>
<script>
const chat = document.getElementById('chat');
const form = document.getElementById('form');
const input = document.getElementById('message');
const username = prompt('Enter your name:') || 'Anonymous';
const ws = new WebSocket(`ws://${window.location.host}`);
ws.onopen = () => addMessage('🟢 Connected to server');
ws.onmessage = (event) => addMessage(event.data);
form.addEventListener('submit', (e) => {
e.preventDefault();
if (input.value.trim()) {
ws.send(`${username}: ${input.value}`);
input.value = '';
}
});
function addMessage(message) {
const div = document.createElement('div');
div.textContent = message;
chat.appendChild(div);
chat.scrollTop = chat.scrollHeight;
}
</script>
</body>
</html>
✅ What this does:
- Connects to the WebSocket server.
- Sends user messages to the server.
- Displays incoming messages instantly.
It’s minimal but fully functional.
5. Running the Chat App
Start your server:
node server.js
Then open http://localhost:3000 in two different browser tabs.
Type a message in one tab, and it’ll instantly appear in both.
🎉 You now have a working real-time chat app built entirely with pure JavaScript!
6. How It Works
Let’s recap what’s happening behind the scenes:
- Each browser connects to the WebSocket server using:
const ws = new WebSocket('ws://localhost:3000');
2. The connection is upgraded from HTTP → WebSocket.
3. When a user sends a message:
ws.send('Hello everyone!');
4. The server receives it and sends it to all other connected clients:
wss.clients.forEach(client => client.send(message));
5. Each client receives the message via:
ws.onmessage = (event) => addMessage(event.data);
And that’s it, real-time chat, zero frameworks.
7. Enhancing the Chat App
Now that the core works, let’s make it feel more alive.
1. Add Join and Leave Notifications
wss.on('connection', (socket) => {
wss.clients.forEach((client) => {
if (client.readyState === 1) client.send('🟢 A new user joined');
});
socket.on('close', () => {
wss.clients.forEach((client) => {
if (client.readyState === 1) client.send('🔴 A user left');
});
});
});
2. Add Timestamps
const time = new Date().toLocaleTimeString();
ws.send(`[${time}] ${username}: ${input.value}`);
3. Handle Errors Gracefully
ws.onerror = (err) => addMessage('⚠️ Connection error');
These small touches make a huge difference in user experience.
8. Debugging Tips
If you run into issues:
- Make sure your server is running on port
3000. - Use
console.log()on both the server and the client to trace messages. - WebSockets don’t use CORS, so no need for CORS configuration.
- If you deploy under HTTPS, use
wss://instead ofws://.
9. Why This Works So Well
This setup is:
- Fast one persistent connection, no polling.
- Lightweight under 100 lines of code total.
- Scalable, easily extended with rooms, authentication, or databases.
Once you understand pure WebSockets, you can confidently move on to Socket.IO or other real-time frameworks, knowing exactly what they’re abstracting away.
10. Next Steps
You can extend this app in multiple directions:
- Add rooms: Group chats with different channels.
- Persist messages: Save chat logs in a database.
- Add authentication: Identify users securely.
- Add typing indicators: Show when a user is typing.
- Add emojis or Markdown support.
Each of these can still work on top of your existing WebSocket infrastructure.
Conclusion
You just built a complete real-time chat app using WebSockets and pure JavaScript from scratch.
Now you understand how instant messaging really works:
- A persistent, two-way connection
- Simple event-based message handling
- Lightweight communication without HTTP overhead
Pro Tip: Start simple. Once you master basic WebSocket flow, scaling to thousands of users or integrating databases becomes straightforward.
Call to Action
Did you follow along and get your chat app working?
Share your version, ideas, or improvements in the comments and bookmark this post for your next real-time JavaScript project.


Leave a Reply