Watch YouTube together

An example where everyone in the chat room watches a YouTube video picked by a moderator or higher.

Last updated October 19, 2026

On this page

When a moderator or higher enters a YouTube URL, the same video appears for everyone in the chat room. The video ID is sent over a channel and saved, so people who join later also see the current video.

Code

Put the code below on your page as is and it works. The first line is a pre-registration snippet that attaches events before uchat.js loads (Async loading and multiple chat rooms).

<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:400px;height:500px"></u-chat>

<script>
U.chat('my-room').on('after.create#youtube-demo', function (room) {
	var ui = room.skin;
	var channel = room.plugin.add('youtube-demo');
	var removeOverlay = null;

	function show(id) {
		if (removeOverlay) removeOverlay();
		removeOverlay = null;
		if (!id) return;
		removeOverlay = ui.mount.overlay({
			id: 'youtube-demo.player',
			layer: 'content-overlay',
			scope: 'message-list',
			mount: function (host) {
				host.style.pointerEvents = 'none'; // Let clicks on empty space reach the chat below
				var frame = host.ownerDocument.createElement('iframe');
				frame.src = 'https://www.youtube-nocookie.com/embed/' + id + '?autoplay=1';
				frame.allow = 'autoplay; encrypted-media';
				frame.style.cssText =
					'position:absolute;top:8px;right:8px;width:320px;height:180px;border:0;pointer-events:auto';
				host.appendChild(frame);
				return function () {
					frame.remove();
				};
			},
		});
	}

	function videoId(text) {
		var m =
			/(?:youtu\.be\/|v=|embed\/|shorts\/)([A-Za-z0-9_-]{11})/.exec(text) ||
			/^([A-Za-z0-9_-]{11})$/.exec(text);
		return m ? m[1] : '';
	}

	// Receive: only from moderators or higher, or from the server (saved value, server API)
	channel.onReceived(function (room, data) {
		if (!data.user || data.user.auth < 2) return;
		var id = String(data.data[0] || '');
		if (id === '' || /^[A-Za-z0-9_-]{11}$/.test(id)) show(id);
	});

	ui.menu.add({
		id: 'youtube-demo.open',
		label: 'YouTube',
		condition: function (ctx) {
			return ctx.room.my.auth >= 2;
		},
		onSelect: function () {
			ui.dialog.prompt('YouTube', 'Enter a video URL. Leave it empty to close.').then(function (text) {
				if (text === null) return; // Canceled
				text = text.trim();
				var id = videoId(text);
				if (text && !id) return ui.dialog.alert('YouTube', 'Not a valid YouTube URL.');
				if (id) {
					channel.save([id]); // Save for people who join later
					channel.send([id]); // To everyone here now (it comes back to me too)
				} else {
					channel.clear();
					channel.send(['']);
				}
			});
		},
	});
});

// Recalculate the button's display condition on join or when permissions change
function refreshMenu(room) {
	room.skin.menu.resolver.refresh();
}
U.chat('my-room').on('after.join#youtube-demo', refreshMenu);
U.chat('my-room').on('after.user.modi#youtube-demo', refreshMenu);
</script>

Usage

  • Moderators and higher see a YouTube button in the input toolbar. If your chat room doesn't use member integration, become an admin with /login adminpassword to try it.
  • Enter a URL and a player appears at the top right of the chat area. Enter an empty value and it closes for everyone.
  • People who join later receive the saved value on join and see the same video.

How it works

Step API used Description
Button room.skin.menu.add condition shows it only to moderators and higher. Callbacks must be synchronous
Input room.skin.dialog.prompt A Promise that returns the entered text on OK, or null on cancel
Send channel.send, channel.save send goes to everyone connected now (including you); save stores it for people who join later
Display room.skin.mount.overlay Attaches an area overlaid on the message list, and detaches it with the returned function

The server sends the saved value with permission 4 on join, so it passes the receiver's permission check. For details on each API, see Skin UI API and Channel API and limits.

Play from the server

Send a video ID to the same channel with the Server REST API and you can play it from places like your site's admin screen. Data sent by the server has permission 4, so it passes the permission check in the code above.

curl -X POST https://api.uchat.io/v2/rooms/room_xxxxxxxxxxxxxxxx/events \
  -H "Authorization: Bearer uchat_..." \
  -H "Content-Type: application/json" \
  -d '{"plugin":"youtube-demo","save":true,"data":["dQw4w9WgXcQ"]}'

If you send with "save": true, the value is saved in the chat room, so people who join later see the same video. It is saved even if you send it when no one is in the chat room. To close the video, save "data":[""]. Issue the API key in the API menu of the admin page.

Things to note

  • Anyone can send data to the same channel. Always check the sender's permission (data.user.auth) and the shape of the data you receive.
  • So the overlay doesn't block the chat, let clicks pass through the host and give pointer-events:auto only to the player.
  • Only moderators and higher can use save. A free chat room can save to only one channel, so saving to another channel deletes the existing saved value.
  • Free chat rooms have a limit on channel traffic. A send to everyone counts once per connected user, and going over gives error 428 (Channel API and limits).

Still not sure how it works? Ask your question on the contact board.

Business name UCHAT · CEO 김경욱 · Business registration no. 141-05-32832
Address 경기도 파주시 와석순환로 61 · Mail-order business registration no. 2013-경기파주-4485호
Phone 010-8677-8633 · Email support@uchat.io

ⓒ 2013–2026 UCHAT. All rights reserved.