-
-
Save juanbrujo/72c45057b104bd5986ebe54d1241dce5 to your computer and use it in GitHub Desktop.
| // ==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); | |
| } | |
| }); | |
| })(); |
my literal brain rn obsessing over the weird mystery boxes completely clogging up the conveyor belt in marble sort fr
Finally, a simple fix for one of YouTube’s most annoying “features.” This script feels like something that should’ve been built-in years ago—clean, lightweight, and it just works without messing with anything else. I especially like how it hooks into YouTube’s internal navigation instead of relying on clunky DOM hacks. Huge respect to the author for keeping it efficient and thoughtful—makes live streams way more watchable without the constant chat distraction.
Honestly, this feels less like a workaround and more like restoring common sense to YouTube. It’s impressively streamlined—no bloat, no weird side effects—just a clean solution that does exactly what you want. The fact that it integrates with YouTube’s own navigation instead of patching things together makes it stand out even more.
This is one of those small tweaks that ends up making a surprisingly big difference. YouTube’s default behavior of auto-expanding live chat can be distracting, especially when you’re just there for the content. I like how this script hooks into YouTube’s internal page data rather than relying on brittle DOM hacks—it feels more reliable across navigation changes.
This userscript is a clean and efficient solution for improving focus during YouTube live streams by automatically hiding the live chat by default. Instead of removing the chat entirely, it smartly modifies YouTube’s internal page data so the chat loads in a collapsed state, reducing distractions while still keeping it accessible when needed. By working directly with YouTube’s navigation events and UI state, it ensures the setting applies consistently across both refreshed and dynamically loaded pages.
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
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.
Such a simple script, but it fixes one of the most annoying YouTube behaviors. I love how it hooks into YouTube’s internal events instead of relying on clunky DOM Bflix hacks—makes it feel much more reliable across navigation. The attention to preserving toggle state is a really nice touch too. Clean, smart, and actually useful.