The full road we traveled — the architecture, every build phase and, above all, the why behind each piece. Made so you can retrace the steps and have your own DJ in your own room.
There are two roads here — and it's worth picking one before you get your hands dirty. Chapter 03 (The build phases) builds the browser version: an invisible Chromium that reads the room's page. It's the most educational way to understand each piece, and it's how we started.
But today we don't use a browser anymore. The version that actually runs is a WebSocket client ~10x lighter, and it reuses almost everything from here — only the way it talks to the chat changes. If you follow the phases in order, you'll build the Chromium first and only later find out it became unnecessary.
Want to go straight to the current method? Jump to The turning point — the golden path: it takes you from zero to on air on its own, reusing the phases that didn't change.
Before the steps, understand the design. Knowing what each piece solves, you adapt instead of copying blindly.
A separate IMVU account becomes the "bot". It runs 24/7 on a free virtual machine that does two things at once: it broadcasts a music radio (an audio stream) and keeps an invisible browser logged into your room, reading the chat and obeying commands. IMVU points at the radio's address; the people in the room hear the music and talk to the bot.
Each piece solves a real problem we only discovered the hard way. The sections below explain every choice — read the "whys", they're worth more than the code. Note: we started with an invisible browser (Chromium); later it evolved into a browserless client, much lighter — the chapter The turning point tells how and why (and why the "bot = Chromium" in the diagram is the original version).
What to have at hand. Nothing here costs money.
Install the PC tools in one go. Pick your system, click copy and paste it into your terminal (the page doesn't run anything for you — you see the command before running it).
# Windows 10/11 - winget comes built in (Terminal or PowerShell)
winget install OpenJS.NodeJS.LTS
winget install yt-dlp.yt-dlp
winget install Gyan.FFmpeg
winget install Git.Git# macOS - via Homebrew (install brew first, at brew.sh)
brew install node yt-dlp ffmpeg git# Debian/Ubuntu - apt's Node may be old; for v18+ see nodejs.org
sudo apt update
sudo apt install -y nodejs npm ffmpeg git python3-pip
python3 -m pip install -U yt-dlpKeep secrets (the bot's login session, the Gemini key, the DuckDNS token) out of version control. A .gitignore with auth.json, *.key, *.token and cache/ keeps you from leaking everything on the first git push.
Order matters — each phase builds on the previous one. Build and test on your PC first; only then move to the cloud.
At heart, the bot is an automated browser. Use Playwright to open a Chromium, log into the bot account once and save the session (a login.js that writes an auth.json). After that you just reopen the room reusing that session.
Inside the room, IMVU talks over a WebSocket (the "IMQ"). The bot listens to the messages arriving through it — that's how it "reads" the chat and detects commands. Start simple: log in, join the room, print every message that shows up to the terminal.
IMVU has no public chat API. The reliable way to take part in a room is to be a real client — a logged-in browser. Headless (invisible) so it can run on a server with no screen.
A simple HTTP server that broadcasts a continuous MP3 on a port (e.g. :8000). It has a folder of "filler" songs (the ones that play on their own), shuffles the order, and exposes internal routes to queue and skip tracks (/add, /skip, /status). The bot talks to those routes over localhost.
Later you'll want to restart the bot (it builds up memory) without cutting the music. Keeping the radio as its own service keeps the sound going even while the bot restarts.
Now connect what the bot reads from the chat to the jukebox. A message like !play song name makes the bot call the stream over localhost and queue it. !skip, !queue etc. follow the same idea.
To play "any song", the bot needs to download from YouTube — and that's where the biggest trap lives: YouTube blocks datacenter IPs, so the VM can't download on its own. The fix is a small HTTP agent on your home PC (a residential IP, which YouTube accepts) running yt-dlp. The VM reaches that agent through a reverse SSH tunnel.
This means !play/!add only work while your PC is on and the bridge is running. Everything else (radio, chat, AI) works without it. If you don't want that dependency, you can play only the local music folder and skip YouTube.
Create an Always Free instance (the E2.1.Micro, 1 GB of RAM, is the easiest to get; the ARM A1 gives more room if a slot opens up). Ubuntu. Install Node, Playwright's Chromium and ffmpeg, send the files over scp, and register both services with systemd so they start at boot and restart on their own.
# skeleton of a systemd service (/etc/systemd/system/bot.service)
[Service]
ExecStart=/usr/bin/node /home/ubuntu/bot/bot.js
Restart=always
WorkingDirectory=/home/ubuntu/bot
Chromium alone eats ~500 MB. Turn on swap (e.g. 2 GB in /swapfile) or the VM freezes. And forget 3D/WebGL on this machine — we tried, it ran out of memory. Run the browser without 3D.
IMVU Next is an HTTPS page and refuses plain-HTTP stream addresses — TLS is mandatory. But Let's Encrypt won't issue a certificate for a bare IP, and that's why DuckDNS is here: it gives you a free domain pointing at the VM's IP. In front of everything sits Caddy, which gets the HTTPS certificate automatically and passes traffic to the stream.
# Caddyfile — forwards 443 to the local stream
your-name.duckdns.org {
reverse_proxy localhost:8000 {
flush_interval -1 # ESSENTIAL: without it Caddy holds the buffer and the sound stalls
}
}
On Oracle, a port only opens if both allow it: the system firewall (iptables) and the OCI "Security List". Open 22, 80 and 443; keep 8000 closed to the public (only Caddy talks to it from inside). And remember: Oracle's IP is ephemeral — a cron updating DuckDNS every 5 min keeps the domain right if the IP changes.
In your IMVU room's edit panel, paste the stream URL (https://your-name.duckdns.org/stream) into the music field. Done: whoever joins the room already hears the radio. The bot, logged into the room owner's account, shows up in the chat.
A bot that drops at 3 a.m. is no use. These defenses were born, one by one, from real problems:
Almost this whole list came from "why did she vanish again?". Write down your own symptoms — what looks like a bug is almost always IMVU or Chromium reacting to something specific.
With the base standing, the rest is flavor. Hook up a model (we use Gemini, it has a free tier) to give replies with a persona; let the bot decide on its own when a message is meant for it (answering "skip" when it isn't, so it doesn't butt in). You can add greetings, memory of who has visited, knowledge of the room itself/its furniture and a continuous conversation window.
If you use a model with a name like -latest, know that it "rolls forward" on its own and may one day break silently (it happened here). Pin a specific version and test after any switch.
The phases above are how we started — and they taught everything. This chapter is what we run today, and it stands on its own: further down there's a step-by-step from zero that reuses the phases that didn't change and calmly teaches the one part that actually did.
The Phase 0 bot was an invisible Chromium driven by Playwright. It worked, but it was a very expensive messenger — a whole browser just to carry a few notes. Then it clicked: IMVU's "presence" in the room is just a protocol — a WebSocket (the IMQ) plus a few REST calls. You can speak that protocol directly, with no browser at all.
That's what we did: reverse engineering of the protocol — watching the frames a real browser exchanges with IMVU, starting from an old build found on GitHub and polishing it for our use — and we wrote a browserless client that logs in, joins the room, reads the chat, sits, talks and checks the participants by speaking raw IMQ. Same account, same room, same commands — zero Chromium.
Chromium alone ate ~500 MB (and leaked until it ran out of memory at around ~19 h of uptime). The pure Node client sits at ~50 MB. On top of that, ALL the hacks that only existed because of the browser disappear: the mandatory swap, the "forget 3D/WebGL", the "load-bearing" anti-throttle flags, the frozen-page watchdog and the "breather" that restarted just to clear memory. Without a browser, none of that needs to exist.
With the bot this light, that "1 GB is too little" agony evaporates — and it's worth saying out loud: you may not even need a VM. A decent home PC (a Windows 11 with 16 GB of RAM, say) runs the bot, the stream and everything else locally, with room to spare. The free cloud was really about having an always-on machine and a public address; if your home already covers that, the whole skeleton above — Oracle, swap, systemd — becomes optional. Use the cloud if it solves a problem for you, not out of obligation. The phases above are the cheapest road, not the only one.
And the best part: the browserless client reused almost everything. The radio/jukebox (server.js), the YouTube bridge, the AI layer and the room defenses never depended on Chromium — only the "browser" half of the bot became "direct protocol". The other phases still hold word for word. And it was that lightness that made room to grow later (playing in several rooms at the same time, for example, without the machine breaking a sweat).
You don't need to build the Chromium to get here. If you're starting now and just want what runs today, follow this trail: the steps that didn't change point back to the phases up above (it's literally the same), and the middle step — the new heart — is explained calmly.
The IMQ is not documented. You figure out the event format by watching a real login (the browser's network tools, WebSocket tab) or by starting from a reference build — that's what we did, finding an old one on GitHub and polishing it. Set aside time for this part; it's the only "detective" stretch of the project.
Speaking the protocol directly is light and fast, but it's an unofficial protocol: if IMVU changes the IMQ frames, your client breaks and you fix it by hand (a real browser would adapt on its own). It's a conscious trade — much lighter, a tad more fragile. Here it was well worth it.
If you read only one section, read this one. Every line cost hours of debugging.
Roughly, who does what. The names are the ones we used — yours may differ.