Zero-friction chat app—no signup, link-only access, anonymous "Agent-X" names, set duration for room, auto room dispose once time is over, no data is saved/stored, pure anonymity, live language switching (history bulk-translates + new messages stream). Hacker terminal UI. V1 complete github

Live chat with users sidebar, countdown timer, translation status.
Hey devs!, I wanted to level up my skills, so I combined Socket.io + real-time multilingual chat + cyberpunk UI. Meet TERMINAL47.
The Flow: Share link → Join as anonymous agent → Chat → Switch English→Japanese mid-convo → All past messages instantly translate → New messages are translated in real time →Timer expires → Room erased forever.
🛠️ Tech Stack (Production Architecture)
Frontend: Next.js 16 + TypeScript
Backend: Express + Socket.io + Lingo.dev SDK
State: useState (local) + localStorage (userName)
Storage: None (pure ephemeral)
Deployment: Local dev → GitHub
Step 1: Backend Setup (index.js)
Clean Express + Socket.io server with dedicated translation endpoints:
// index.js
import express from "express";
import { createServer } from "http";
import { Server } from "socket.io";
import socketEvent from "./socket/events.js"; // Room logic here
import languageTranslationBulk from "./utils/translationBulk.js";
import languageTranslationSingle from "./utils/translationSingle.js";
const app = express();
const server = createServer(app);
const io = new Server(server, { cors: { origin: process.env.CLIENT_URL }});
app.use(cors());
app.use(express.json());
app.post("/auth/translation/bulk", async (req, res) => {
const { messages, currentLanguage, translateTo } = req.body;
const languageTranslationFunction = await languageTranslationBulk(
messages, currentLanguage, translateTo
);
res.json({ success: true, data: languageTranslationFunction });
});
app.post("/auth/translation/chunk", async (req, res) => {
const { message, currentLanguage, translateTo } = req.body;
const languageTranslationFunction = await languageTranslationSingle(
message, currentLanguage, translateTo
);
res.json({ success: true, data: languageTranslationFunction });
});
socketEvent(io); // Room management
Step 2: Lingo.dev Integration
Bulk translation (history on language switch):
// utils/translationBulk.js -
import { LingoDotDevEngine } from "lingo.dev/sdk";
const languageTranslationBulk = async (messages, currentLanguage, translateTo) => {
const lingoDotDev = new LingoDotDevEngine({ apiKey: process.env.LINGO_DEV_API_KEY });
const userMessages = messages.filter((msg) => msg.text != "");
const translated = await lingoDotDev.localizeText(userMessages, {
sourceLocale: currentLanguage,
targetLocale: translateTo,
});
return translated;
};
Step 3: Frontend Hook (useChat.ts)
// hooks/useChat.ts
export const useChat = () => {
const [socket, setSocket] = useState<Socket | null>(null);
const [roomStatus, setRoomStatus] = useState<boolean | null>(null);
const [expiresAt, setExpiresAt] = useState<number | null>(null);
const joinRoom = useCallback((roomId: string, userName: string) => {
socket?.emit("join_room", { roomId, userName }, (result: any) => {
if (result.success && result.room) {
const expiry = Date.now() + result.room.timeRemaining;
setExpiresAt(expiry);
}
});
}, [socket]);
const sendMessage = useCallback((roomId: string, message: string) => {
socket?.emit("send_message", { roomId, message });
}, [socket]);
return { socket, joinRoom, sendMessage, roomStatus, expiresAt };
};
Step 4: 🔥 Live Translation Magic (Chat.tsx)
Hero feature—language switch + bulk/single translation:
useEffect(() => {
if (!allMessages.length || language === lastLanguage) return;
setIsTranslating(true);
const translateChat = async () => {
const response = await fetch(`${process.env.NEXT_PUBLIC_BACKEND_URL}/auth/translation/bulk`, {
method: "POST",
body: JSON.stringify({
messages: allMessages,
currentLanguage: lastLanguage,
translateTo: language,
}),
});
const data = await response.json();
setAllMessages(data.data); // All history translated!
};
translateChat();
}, [language]);

Switch language → History bulk-translates + new messages auto-translate.
Step 5: SideNavBar (Users + Timer)
// SideNavBar.tsx - Key features
const SideNavBar = ({ userCount, expiresAt, language, setLanguage, isTranslating }) => {
const [formattedTime, setFormattedTime] = useState("00:00");
useEffect(() => {
if (!expiresAt) return;
const tick = () => {
const remainingMs = expiresAt - Date.now();
const totalSeconds = Math.max(0, Math.floor(remainingMs / 1000));
const minutes = Math.floor(totalSeconds / 60);
setFormattedTime(`${minutes.toString().padStart(2, '0')}:${(totalSeconds % 60).toString().padStart(2, '0')}`);
};
tick();
const interval = setInterval(tick, 1000);
return () => clearInterval(interval);
}, [expiresAt]);
return (
<aside className="w-12 md:w-64 bg-secondaryBackground">
<div>USERS- {userCount}</div>
<LanguageSelection language={language} setLanguage={setLanguage} />
<div className="timer">{formattedTime}</div> {/* Red border when <60s */}
</aside>
);
};
🚀 V1 Complete ✅
✅ Real-time chat (Socket.io)
✅ User presence + system messages
✅ Live translation (bulk + streaming)
✅ Room expiry countdown
✅ Hacker terminal UI
✅ Anonymous access (no auth)
✅ TypeScript everywhere
Next: Typing indicators with Redis V2? Clone the repo and try language switching—feels like magic! 🪄
Github
Top comments (0)