In most multiplayer games, when a player disconnects, their character simply vanishes. This makes the world feel empty and temporary. We decided to change that by building a Persistent Ghost System.
Now, when you leave our world, your avatar stays exactly where you left it for 12 hours, serving as a digital footprint of your visit.
The Problem: The “Empty Room” Syndrome
3D social spaces feel lonely if you aren’t there at the exact same time as someone else. We wanted a way for players to “leave their mark,” allowing the world to look populated even during off-peak hours.
The Solution: Ghost Conversion
We implemented a server-to-client bridge that handles “ghosting” in three tiny but powerful steps.
1. Server-Side: The 12-Hour Timer
Instead of deleting the player data on a disconnect event, the server rebrands the player. We change their ID to a ghost_ prefix and start a long-running timer.
code JavaScript
// On Disconnect: Convert to Ghost
const ghostId = "ghost_" + socket.id;
players[ghostId] = players[socket.id]; // Keep the data
delete players[socket.id]; // Remove the active socket
// Set the 12-hour expiration
setTimeout(() => {
delete players[ghostId];
io.emit('player_left', ghostId);
}, 12 * 60 * 60 * 1000);2. Client-Side: Visual “Offline” Cues
To make sure active players can distinguish between live users and ghosts, we added visual shaders. When the client sees an ID starting with ghost_, it automatically:
- Sets the model to 50% opacity.
- Appends (OFFLINE) to the name tag.
- Forces the model into an Idle state.
code JavaScript
if (id.startsWith('ghost_')) {
child.material.transparent = true;
child.material.opacity = 0.5; // Visual hint that the player is gone
}3. Physical Presence (No Ghosting)
Even though these characters are “offline,” they aren’t just holograms. We kept the Collision Detection active against them. You cannot walk through a ghost. This creates a sense of solid, physical history in the world—if a group of friends stood in a circle before logging off, that circle remains as a physical barrier for 12 hours.
Why it works
- Anti-Cloning: If a player logs back in, the server automatically finds and “kills” their old ghost before spawning their new live character. This prevents the world from becoming cluttered with clones of the same person.
- Memory Efficiency: By using a simple Map and setTimeout, the server handles the persistence without needing a heavy database.
- Social Proof: New players entering the world see a crowd of characters, making the project feel popular and alive.
Conclusion
Persistence doesn’t always require a massive database or a 24/7 server farm. With just a few lines of logic and some clever ID prefixing, we turned a temporary room into a persistent world where your presence actually matters.
Next time you log in, say hi to the ghosts of the players who came before you!