A minimal, dark-themed real-time chat UI built with vanilla HTML, CSS, and JavaScript.
Hallway's frontend is a single index.html file — no build step, no framework, no dependencies beyond two Google Fonts. It connects to the Hallway Go backend over WebSocket and handles real-time messaging, user count updates, and system announcements.
- Real-time messaging via WebSocket — messages appear instantly across all connected clients
- Chat history replay — on connect, the last 100 messages are loaded automatically
- Live user count — the header updates as people join and leave
- System announcements — join/leave events are shown inline in a distinct style
- Anonymous fallback — if no username is entered, the server assigns an
Anon_HHmmssname - Smart auto-scroll — only scrolls to the bottom if you're already near it; scrolling up to read history won't interrupt you
- Mobile responsive — full-screen layout on small screens, safe area insets for notched phones, landscape support
- No build step — open the file directly or serve it statically
You can open index.html directly in a browser, or serve it via the Go backend:
hallway/
└── index.html
In index.html, find this line near the bottom:
const socket = new WebSocket(`${protocol}://hallwayserver.fly.dev/chat?username=...`);Change the hostname to match your backend:
| Environment | URL |
|---|---|
| Local dev | localhost:8080 |
| Fly.io deploy | hallwayserver.fly.dev |
| Custom domain | your-domain.com |
The protocol (ws:// vs wss://) is selected automatically based on whether the page is served over HTTP or HTTPS.
The frontend speaks a simple JSON protocol over WebSocket.
Sending a message:
{
"type": "chatMessage",
"payload": { "username": "alice", "message": "Hello!" }
}Receiving — chat message:
{ "type": "chatMessage", "payload": { "username": "alice", "message": "Hello!" } }Receiving — system event:
{ "type": "system", "payload": { "text": "alice joined" } }Receiving — user count:
{ "type": "userCount", "payload": { "count": 3 } }The server always overwrites the
usernamefield with the server-resolved name, so anonymous users always display correctly.
Works in all modern browsers (Chrome, Firefox, Safari, Edge). Requires WebSocket support — available in every browser released after 2012.