Size
7.0 KB
Version
1.0.1
Created
Dec 24, 2025
Updated
28 days ago
1// ==UserScript==
2// @name YouTube Speed Controller 12x
3// @description Control YouTube video playback speed up to 12x
4// @version 1.0.1
5// @match https://*.youtube.com/*
6// @icon https://www.youtube.com/s/desktop/a3c20ab4/img/favicon_32x32.png
7// ==/UserScript==
8(function() {
9 'use strict';
10
11 console.log('YouTube Speed Controller 12x initialized');
12
13 // Speed options from 0.25x to 12x
14 const speedOptions = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.5, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
15
16 function createSpeedControl() {
17 console.log('Creating speed control UI');
18
19 // Remove existing control if present
20 const existingControl = document.getElementById('custom-speed-control');
21 if (existingControl) {
22 existingControl.remove();
23 }
24
25 // Find the video element
26 const video = document.querySelector('video');
27 if (!video) {
28 console.log('Video element not found');
29 return;
30 }
31
32 // Create speed control container
33 const speedControl = document.createElement('div');
34 speedControl.id = 'custom-speed-control';
35 speedControl.style.cssText = `
36 position: fixed;
37 bottom: 100px;
38 right: 20px;
39 background: rgba(0, 0, 0, 0.9);
40 color: white;
41 padding: 15px;
42 border-radius: 8px;
43 z-index: 9999;
44 font-family: 'Roboto', Arial, sans-serif;
45 font-size: 14px;
46 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
47 min-width: 200px;
48 `;
49
50 // Create title
51 const title = document.createElement('div');
52 title.textContent = 'Playback Speed';
53 title.style.cssText = `
54 font-weight: bold;
55 margin-bottom: 10px;
56 font-size: 16px;
57 text-align: center;
58 `;
59 speedControl.appendChild(title);
60
61 // Create current speed display
62 const currentSpeedDisplay = document.createElement('div');
63 currentSpeedDisplay.id = 'current-speed-display';
64 currentSpeedDisplay.textContent = `Current: ${video.playbackRate}x`;
65 currentSpeedDisplay.style.cssText = `
66 text-align: center;
67 margin-bottom: 10px;
68 color: #3ea6ff;
69 font-weight: bold;
70 `;
71 speedControl.appendChild(currentSpeedDisplay);
72
73 // Create speed buttons container
74 const buttonsContainer = document.createElement('div');
75 buttonsContainer.style.cssText = `
76 display: grid;
77 grid-template-columns: repeat(3, 1fr);
78 gap: 5px;
79 max-height: 300px;
80 overflow-y: auto;
81 `;
82
83 // Create buttons for each speed option
84 speedOptions.forEach(speed => {
85 const button = document.createElement('button');
86 button.textContent = `${speed}x`;
87 button.style.cssText = `
88 background: #3ea6ff;
89 color: white;
90 border: none;
91 padding: 8px 12px;
92 border-radius: 4px;
93 cursor: pointer;
94 font-size: 13px;
95 font-weight: 500;
96 transition: all 0.2s;
97 `;
98
99 // Highlight current speed
100 if (video.playbackRate === speed) {
101 button.style.background = '#ff3e3e';
102 }
103
104 button.addEventListener('mouseenter', () => {
105 if (video.playbackRate !== speed) {
106 button.style.background = '#5ab3ff';
107 }
108 });
109
110 button.addEventListener('mouseleave', () => {
111 if (video.playbackRate !== speed) {
112 button.style.background = '#3ea6ff';
113 }
114 });
115
116 button.addEventListener('click', () => {
117 video.playbackRate = speed;
118 currentSpeedDisplay.textContent = `Current: ${speed}x`;
119 console.log(`Speed changed to ${speed}x`);
120
121 // Update button styles
122 buttonsContainer.querySelectorAll('button').forEach(btn => {
123 btn.style.background = '#3ea6ff';
124 });
125 button.style.background = '#ff3e3e';
126 });
127
128 buttonsContainer.appendChild(button);
129 });
130
131 speedControl.appendChild(buttonsContainer);
132
133 // Add close button
134 const closeButton = document.createElement('button');
135 closeButton.textContent = '×';
136 closeButton.style.cssText = `
137 position: absolute;
138 top: 5px;
139 right: 5px;
140 background: transparent;
141 color: white;
142 border: none;
143 font-size: 24px;
144 cursor: pointer;
145 padding: 0;
146 width: 25px;
147 height: 25px;
148 line-height: 20px;
149 `;
150 closeButton.addEventListener('click', () => {
151 speedControl.style.display = 'none';
152 });
153 speedControl.appendChild(closeButton);
154
155 // Add toggle button
156 const toggleButton = document.createElement('button');
157 toggleButton.id = 'speed-control-toggle';
158 toggleButton.textContent = '⚡';
159 toggleButton.title = 'Speed Control';
160 toggleButton.style.cssText = `
161 position: fixed;
162 bottom: 100px;
163 right: 20px;
164 background: rgba(0, 0, 0, 0.9);
165 color: white;
166 border: none;
167 width: 50px;
168 height: 50px;
169 border-radius: 50%;
170 cursor: pointer;
171 font-size: 24px;
172 z-index: 9998;
173 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
174 display: none;
175 `;
176 toggleButton.addEventListener('click', () => {
177 speedControl.style.display = 'block';
178 toggleButton.style.display = 'none';
179 });
180
181 document.body.appendChild(speedControl);
182 document.body.appendChild(toggleButton);
183
184 console.log('Speed control UI created successfully');
185 }
186
187 // Wait for video element to be available
188 function waitForVideo() {
189 const video = document.querySelector('video');
190 if (video) {
191 console.log('Video element found');
192 createSpeedControl();
193 } else {
194 console.log('Waiting for video element...');
195 setTimeout(waitForVideo, 1000);
196 }
197 }
198
199 // Initialize when page loads
200 if (document.readyState === 'loading') {
201 document.addEventListener('DOMContentLoaded', waitForVideo);
202 } else {
203 waitForVideo();
204 }
205
206 // Re-create control on navigation (YouTube is a SPA)
207 let lastUrl = location.href;
208 new MutationObserver(() => {
209 const currentUrl = location.href;
210 if (currentUrl !== lastUrl) {
211 lastUrl = currentUrl;
212 console.log('URL changed, re-initializing speed control');
213 setTimeout(waitForVideo, 2000);
214 }
215 }).observe(document.body, { childList: true, subtree: true });
216
217})();