Sending and receiving real-time data
How to use channels (room.plugin) to exchange non-chat data between people in the same chat room, and how to validate the data you receive.
Last updated October 17, 2026
On this page
Channels let people in the same chat room exchange data other than chat messages in real time. Use them for features that keep everyone's screen in sync, such as watch-together, games, and polls. The authoritative list of functions and limits is Channel API and limits.
Open a channel
room.plugin.add(channelName) returns the channel object for that name. Calling it again with the same name returns the same object. People who use the same channel name exchange data with each other.
U.chat('my-room').on('after.create', function (room) {
var dice = room.plugin.add('dice');
});
Channel names must start with a letter or digit, contain only letters, digits, _, and -, and be 64 characters or fewer (save is rejected if the name breaks this rule). Prefix the name with your service name so it does not collide with other features.
Send
Data is always sent as an array.
dice.send([4]); // to everyone in the chat room (including you)
dice.sendTo('홍길동', ['hello', 1]); // only to the one person with that nickname
- Array elements can only be strings, numbers, and booleans. Objects, arrays, and
nullare dropped in transit. If you need structure, split it across elements or send it as a string. - The sender also receives data sent with
send. WithsendTo, only the recipient receives it. If the sender is under a hidden mute, both deliver only back to the sender. - Free chat rooms have a limit on the total number of recipients over the last 60 seconds. Going over it causes error 428. For features that send often, reduce how often you send. → Channel API and limits
Receive
dice.onReceived(function (room, msg) {
// msg.user the sender { nick, id, level, auth, ... }
// msg.type 0 sent to everyone, 1 sent only to me
// msg.data the array that was sent
});
Like other events, the callback receives (room, data). The same data also arrives through the before.plugin and after.plugin events (see Events).
Validate received data
Anyone in the chat room can use developer tools to send any value they want on a channel. Always validate on the receiving side.
- Check the number of elements, their types, and value ranges, and discard anything that does not match.
- When putting data on the screen, use
textContentor escape it.room.printinterprets its input as HTML. - To check who sent the data, look at
msg.user.auth(0 guest, 1 member, 2 moderator, 3 admin, 4 server). Do not decide based on the nickname alone. - If a fair result matters (dice, draws), do not trust a value chosen by one person. Have your server decide it and send it through the server API. → Sending notifications from your server to a chat room
Receive input commands
If you register addParser, typing !channelName args... in the default skin's input box passes the argument array to that function. That input is not sent as a chat message. If the function returns false, the input is treated as unhandled and is sent as a normal message.
dice.addParser(function (args) {
// input: !dice → args: []
dice.send([Math.floor(Math.random() * 6) + 1]);
});
Save state
save(array) stores the channel's latest state on the server. People who join later receive the saved value when they enter. clear() deletes the saved value.
- Only moderators and above can save.
- Saving does not send anything to people who are already in the room. To notify them too, also call
send. - The saved value received on entry arrives with
type1 (only to me), and the sender is the server user (auth4, no nickname). You cannot tell who saved it. - There is a size limit on saved data, and in free chat rooms saving to a different channel deletes the saved value of the previous channel. → Channel API and limits
// Save the video ID an admin picked, and also send it to people already here
video.save(['dQw4w9WgXcQ']);
video.send(['dQw4w9WgXcQ']);
Close
destroy() closes the channel and discards the receive and command functions you registered. Calling add again with the same name opens a new channel.
Full code
This example rolls a die and tells everyone when you type !dice.
<script>U=window.U=window.U||{},U.events=U.events||[],U.chat=function(n){return{on:function(e,t){U.events.push([n,e,t])},off:function(e){for(var t=U.events.length;t>0;)U.events[--t][0]==n&&U.events[t][1]==e&&U.events.splice(t,1)}}};</script>
<script async src="https://client.uchat.io/uchat.js"></script>
<u-chat room="my-room" style="display:inline-block; width:100%; max-width:500px; height:500px;"></u-chat>
<script>
function escapeHtml(text) {
return String(text).replace(/[&<>"']/g, function (c) {
return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c];
});
}
U.chat('my-room').on('after.create', function (room) {
var dice = room.plugin.add('dice');
dice.onReceived(function (room, msg) {
var value = msg.data[0];
if (msg.data.length !== 1 || typeof value !== 'number') return;
if (value % 1 !== 0 || value < 1 || value > 6) return;
var nick = msg.user ? msg.user.nick : '?';
room.print(escapeHtml(nick) + ' rolled: ' + value);
});
dice.addParser(function () {
dice.send([Math.floor(Math.random() * 6) + 1]);
});
});
</script>
Larger examples such as watch-together and games are in Watch YouTube together and Rock paper scissors.
Still not sure how it works? Ask your question on the contact board.