Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save juanbrujo/72c45057b104bd5986ebe54d1241dce5 to your computer and use it in GitHub Desktop.

Select an option

Save juanbrujo/72c45057b104bd5986ebe54d1241dce5 to your computer and use it in GitHub Desktop.
YouTube - Hide Live Chat By Default
// ==UserScript==
// @name YouTube - Hide Live Chat By Default
// @namespace https://gist.github.com/lbmaian/94824cef728917a53d3c6e6ea885469c
// @downloadURL https://gist.github.com/lbmaian/94824cef728917a53d3c6e6ea885469c/raw/youtube-hide-livechat.user.js
// @updateURL https://gist.github.com/lbmaian/94824cef728917a53d3c6e6ea885469c/raw/youtube-hide-livechat.user.js
// @version 0.14
// @description Hide live chat by default on live streams
// @author lbmaian
// @match https://www.youtube.com/*
// @exclude https://www.youtube.com/embed/*
// @icon https://www.youtube.com/favicon.ico
// @run-at document-start
// @grant none
// ==/UserScript==
(function() {
'use strict';
const DEBUG = false;
const logContext = '[YouTube - Hide Live Chat]';
var debug;
if (DEBUG) {
debug = function(...args) {
console.debug(logContext, ...args);
}
} else {
debug = function(...args) {}
}
function log(...args) {
console.log(logContext, ...args);
}
function warn(...args) {
console.warn(logContext, ...args);
}
function error(...args) {
console.error(logContext, ...args);
}
// Note: Following all relies on YT internals.
function updateChatData(data, collapsed) {
if (DEBUG) {
debug('data (before)', window.structuredClone(data));
}
const liveChatRenderer = data.liveChatRenderer;
if (liveChatRenderer) { // if no live chat despite #chat existing, e.g. "Live chat replay is not available for this video."
const expandedByDefault = liveChatRenderer.initialDisplayState === 'LIVE_CHAT_DISPLAY_STATE_EXPANDED';
if (expandedByDefault && collapsed) {
if (collapsed) {
log('hiding live chat');
}
debug('data.liveChatRenderer.initialDisplayState:', liveChatRenderer.initialDisplayState,
'=>', 'LIVE_CHAT_DISPLAY_STATE_COLLAPSED');
liveChatRenderer.initialDisplayState = 'LIVE_CHAT_DISPLAY_STATE_COLLAPSED';
}
const toggleButtonRenderer = liveChatRenderer.showHideButton?.toggleButtonRenderer;
if (toggleButtonRenderer) {
if (expandedByDefault) {
debug('data.liveChatRenderer.showHideButton.toggleButtonRenderer.defaultText/toggledText swapped');
[toggleButtonRenderer.defaultText, toggleButtonRenderer.toggledText] =
[toggleButtonRenderer.toggledText, toggleButtonRenderer.defaultText];
}
const isToggled = !collapsed;
if (DEBUG && toggleButtonRenderer.isToggled !== isToggled) {
debug('data.liveChatRenderer.showHideButton.toggleButtonRenderer.isToggled', toggleButtonRenderer.isToggled,
'=>', isToggled);
}
toggleButtonRenderer.isToggled = isToggled;
}
if (DEBUG) {
debug('data (updated)', window.structuredClone(data));
}
return expandedByDefault;
} else {
return false;
}
}
// Navigating to YouTube watch page can happen via AJAX rather than new page load.
// We can monitor this with YT's custom yt-page-data-fetched event,
// which conveniently also fires even for new/refreshed pages.
// yt-navigate-finish would also work (evt.detail.detail) but yt-page-data-fetched fires earlier.
document.addEventListener('yt-page-data-fetched', evt => {
debug('Navigated to', evt.detail.pageData.url);
debug(evt);
const conversationBar = evt.detail.pageData.response?.contents?.twoColumnWatchNextResults?.conversationBar;
debug('yt-page-data-fetched pageData.response contents.twoColumnWatchNextResults.conversationBar (corresponds to #chat.data)',
conversationBar);
// If response doesn't include conversationBar, there won't be a #chat element at all.
if (conversationBar) {
// If #chat element isn't created yet, default collapsed to true.
// Else keep current collapsed status between pages.
// TODO: sometimes for new pages when chat doesn't exist yet, this apparently happens too late?
// (chat already initialized with old data) and chat thus remains open?
// Detect & fix this - use chat.parentComponent (ytd-watch-flexy)'s updatePageData_ or ytd-app's onYtPageDataFetched?
const chat = document.getElementById('chat');
let collapsed;
if (chat) {
collapsed = chat.collapsed;
log('existing #chat', chat, 'collapsed:', collapsed);
} else {
log('no existing #chat, defaulting collapsed: true');
collapsed = true;
}
updateChatData(conversationBar, collapsed);
}
});
})();
@wcofun

wcofun commented May 9, 2026

Copy link
Copy Markdown

This userscript is a clever and practical quality-of-life tweak for anyone who regularly watches live streams on YouTube. Instead of relying on manual clicks every time a stream loads, it automatically collapses the live chat by modifying YouTube’s internal page data before the interface fully renders. That’s a smart approach—it makes the change feel native rather than forced after the fact. Visit

@gmoviestv

Copy link
Copy Markdown

This userscript is a smart quality-of-life upgrade for YouTube live stream viewers who prefer focus over noise. Instead of simply hiding chat after the page loads, it cleverly Punisher hooks into YouTube’s internal page data system to collapse live chat before it becomes a distraction—even during AJAX navigation between videos.

@lasbelaak1

Copy link
Copy Markdown

Nice approach. I’ve seen a lot of userscripts solve this by repeatedly manipulating the DOM after page load, but modifying the page data before the UI fully renders feels much cleaner and less fragile. The handling of YouTube’s AJAX navigation is especially useful since that’s where many older scripts break. I appreciate solutions that focus on reducing distractions without removing functionality entirely. Interestingly, I recently noticed a similar emphasis on user-friendly design while browsing https://phoenixarizonadentistry.com/ different niche, but the same idea of simplifying the experience without getting in the user’s way.

@emily410182

Copy link
Copy Markdown

youtube-hide-livechat.user.js is a userscript designed to automatically hide the live chat panel on YouTube livestreams. It helps create a cleaner, less distracting viewing experience by removing or collapsing the chat whenever a live stream is opened Redzhub Free.

@makeqiang321-cell

Copy link
Copy Markdown

He chose the brick breaker format because it needed only one axis of input — left and right. The trackball was perfect for it.

@makeqiang321-cell

Copy link
Copy Markdown

tried the paint system. Ready to put a disguise to the test? Play Meccha Chameleon online free in your browser. Prefer the one-page overview of all the maps with the play button

@makeqiang321-cell

Copy link
Copy Markdown

right at the top? Head back to the Meccha Chameleon Online homepage.

@makeqiang321-cell

Copy link
Copy Markdown

Bus Traffic Fever turns gridlock into a brain teaser — slide the buses, open the blocked lanes, and pick up every color-matched

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment