This document explains how the "Chat" example leverages RCWeb technology to enable real-time symmetric multi-user communication and peer-to-peer file sharing without a backend database.
Unlike Spacewar's Viewer-Controller architecture, the Chat application uses a symmetric peer-to-peer
model. All connected users run the same app by loading the same HTML page
(chat/index.html) and join the same room. They act as both senders and receivers.
chat.receiveMessage(...)) and broadcasts it via
rc.send(js, "chat"). The RCWeb server proxies this payload to all connected clients in the
room, where comms.js executes it securely, updating the DOM and local state immediately.
chat.refresh(). To prevent everyone from replying out at
once, existing clients schedule a reply timeout inversely proportional to how long they've been
connected. The oldest client fires first, sending the chat history, and appends a
chat.cancelRefresh() command that cancels the pending timeouts on all other clients.
rc.onUpdateClients(...) to maintain a list of known clients in the room. If a user
disconnects, the client generates a local "user left" system message alongside an audio notification.
The disconnected user's past messages remain visible in the chat history. Joined clients broadcast an
"announce join" action which similarly invokes sounds. Additionally, name changes generate system
messages and dynamically update the user's past messages with their new name in the local view.
chat.updateTypingStatus(..., true). Since network jitter can happen, clients maintain their
own 3-second timeout (remoteTypingTimeouts) to automatically clear a user's typing
indicator if they don't receive an update, preventing "ghost typing" states.
<canvas> and stored in memory as a Blob. A special URL proxying pattern
(/x-file/${rc.room}/${rc.client}/${fileId}/${safeFileName}) is broadcast. When
other users request this URL, the RCWeb server asks the originating client via
rc.sendFileChunk to provide the data. The sender PUTs the file chunk-by-chunk
over AJAX back to the server, which streams it to the requesting clients. Because images are served P2P,
if the originating client disconnects, new clients cannot fetch the image. In this case, the image
element handles the error gracefully by replacing the broken image with a fallback placeholder text
"[Image no longer available]".
To recreate this pattern for a new multi-user symmetric app (like a whiteboard, collaborative editor, or group chat), use these features in the RCWeb library:
Your HTML page must configure the rc environment variables. All clients share the same
app (e.g. "chat") and room.
<script>
var rc = {
"version": "${version}",
"app": "${app}",
"room": "${roomId}",
"client": "${clientId}",
"commsWebSocket": "${websocket}"
};
</script>
<script src="/assets/core/comms.js"></script>
Define a global API for other peers to invoke, and use rc.sendFunctionCall to invoke it on other
peers.
This method automatically serializes arguments for safe execution on the target(s).
var myApp = (function() {
return {
// Public API to be executed remotely over RCWeb
receiveMessage: function(client, text) {
if (client === rc.client) return; // Skip our own echo
// Add message to screen...
},
sendMessage: function(text) {
// Predict local change
// Add message to screen...
// Broadcast execution instructions to other "chat" app peers in the room
rc.sendFunctionCall("chat", "myApp.receiveMessage", rc.client, text);
}
};
})();
To serve local Blobs/Files directly to other clients through the RCWeb server proxy, bind to
rc.sendFileChunk:
rc.sendFileChunk = function (fileId, start, url) {
var file = myApp.sharedFiles[fileId];
if (!file) return;
var chunkSize = 1000000; // 1MB chunks
var end = Math.min(file.size, start + chunkSize);
var chunk = file.slice(start, end);
var xhr = new XMLHttpRequest();
xhr.open("PUT", url);
xhr.setRequestHeader("Content-Type", file.type);
xhr.setRequestHeader("Content-Range", "bytes=" + start + "-" + (end - 1) + "/" + file.size);
xhr.send(chunk);
};
// URL format to share with peers:
// "/x-file/" + rc.room + "/" + rc.client + "/" + fileId + "/image.jpg"
Listen to network and client events to maintain the shared room state.
rc.onUpdateClients = function (clients) {
console.log("Currently connected clients:", clients);
// Cleanup state for clients that left...
};
rc.onUpdateNetworkStatus = function (heading, info) {
if (rc.connected && !wasConnected) {
// We just joined, ask peers to sync state!
wasConnected = true;
}
};
Using a symmetric RCWeb setup allows for rapid development of real-time collaborative applications. By using Javascript execution instructions as the transmission payload, you write the syncing logic in the same language and location as your UI logic, bypassing complex backend schemas and APIs. Features like P2P proxying and presence tracking are handled natively by the platform.