Instagram Keyboard Shortcuts for Power User

Scroll through posts with standard J/K keyboard shortcuts. L to like, O to save, U/I to rewind/fast forward video, M to Mute/Unmute, Space to play/pause video. On the Reels page use left/right arrow keys to rewind/fast forward video.

  1. // ==UserScript==
  2. // @name Instagram Keyboard Shortcuts for Power User
  3. // @namespace http://tampermonkey.net/
  4. // @version 2.5.0
  5. // @description Scroll through posts with standard J/K keyboard shortcuts. L to like, O to save, U/I to rewind/fast forward video, M to Mute/Unmute, Space to play/pause video. On the Reels page use left/right arrow keys to rewind/fast forward video.
  6. // @author French Bond
  7. // @match https://www.instagram.com/*
  8. // @grant none
  9. // @require https://code.jquery.com/jquery-3.4.1.min.js
  10. // ==/UserScript==
  11.  
  12. /* globals jQuery, $ */
  13. /* jshint esversion: 6 */
  14.  
  15. let seeAll = true;
  16. let currentArticle = null;
  17. let searchFocused = false;
  18. let scrolling = false;
  19. let slideshowInterval;
  20. let isSlideshow = false;
  21.  
  22. const fastForward = 2;
  23. const rewind = 1;
  24.  
  25. $(function () {
  26. 'use strict';
  27.  
  28. const headerHeight = 10;
  29. const scrollSpeed = 200;
  30.  
  31. function startSlideshow() {
  32. isSlideshow = true;
  33. slideshowInterval = setInterval(() => {
  34. findAndClickButton('Next');
  35. }, 5000);
  36. }
  37.  
  38. function stopSlideshow() {
  39. isSlideshow = false;
  40. clearInterval(slideshowInterval);
  41. }
  42.  
  43. // Disable when user is typing
  44. $('input,textbox,select')
  45. .focus(() => {
  46. searchFocused = true;
  47. })
  48. .blur(() => {
  49. searchFocused = false;
  50. });
  51.  
  52. // Function to determine the current page
  53. function getCurrentPage() {
  54. const url = window.location.href;
  55. if (url === 'https://www.instagram.com/') return 'home';
  56. if (url.includes('/reels/')) return 'reels';
  57. if (url.includes('/p/')) return 'post';
  58. if (url.includes('/saved/')) return 'saved';
  59. if (url.includes('/explore/')) return 'explore';
  60. if (url.includes('/accounts/')) return 'profile';
  61. if (url.includes('/explore/tags/')) return 'tag';
  62. if (url.includes('/explore/locations/')) return 'location';
  63. if (url.includes('/tv/')) return 'igtv';
  64. return 'unknown';
  65. }
  66.  
  67. // Function to check if an element is visible
  68. function isVisible(element) {
  69. if (!element) return false;
  70.  
  71. const style = window.getComputedStyle(element);
  72.  
  73. // Check if the element has zero size
  74. const hasSize = !!(
  75. element.offsetWidth ||
  76. element.offsetHeight ||
  77. element.getClientRects().length
  78. );
  79.  
  80. // Check visibility-related CSS properties
  81. const isNotHidden =
  82. style.display !== 'none' &&
  83. style.visibility !== 'hidden' &&
  84. style.opacity !== '0';
  85.  
  86. return hasSize && isNotHidden;
  87. }
  88.  
  89. // Function to find and control video
  90. function findAndControlVideo(action) {
  91. const video = $(currentArticle).find('video')[0];
  92. if (video) {
  93. switch (action) {
  94. case 'playPause':
  95. if (video.paused) video.play();
  96. else video.pause();
  97. break;
  98. case 'rewind':
  99. video.currentTime -= rewind;
  100. break;
  101. case 'fastForward':
  102. video.currentTime += fastForward;
  103. break;
  104. case 'muteUnmute':
  105. const muteButton = $(currentArticle).find(
  106. '[aria-label="Toggle audio"]'
  107. );
  108. if (muteButton.length) {
  109. muteButton.click();
  110. }
  111. break;
  112. }
  113. }
  114. }
  115.  
  116. function findTopVideo() {
  117. let closestVideo = null;
  118. let closestDistance = Infinity;
  119. $('video').each(function () {
  120. const rect = this.getBoundingClientRect();
  121. const distance = Math.abs(rect.top);
  122. if (distance < closestDistance) {
  123. closestDistance = distance;
  124. closestVideo = this;
  125. }
  126. });
  127. return closestVideo;
  128. }
  129.  
  130. function scrollTo(pageY) {
  131. scrolling = true;
  132. $('html, body').animate(
  133. { scrollTop: pageY },
  134. {
  135. duration: scrollSpeed,
  136. done: () => {
  137. scrolling = false;
  138. },
  139. }
  140. );
  141. }
  142.  
  143. // Function to find and click a button
  144. function findAndClickButton(ariaLabel) {
  145. let button = document.querySelector(`button[aria-label="${ariaLabel}"]`);
  146. if (!button) {
  147. button = document.querySelector(
  148. `button:has(svg[aria-label="${ariaLabel}"])`
  149. );
  150. }
  151. if (button) {
  152. button.click();
  153. }
  154. }
  155.  
  156. // Keyboard shortcuts for Home page
  157. function homeKeyboardShortcuts(e) {
  158. switch (e.keyCode) {
  159. case 65: // A - Toggle see all
  160. seeAll = !seeAll;
  161. break;
  162. case 74: // J - Scroll down
  163. if (seeAll && $(currentArticle).find('[aria-label="Next"]').length) {
  164. $(currentArticle).find('[aria-label="Next"]').click();
  165. } else {
  166. $('article').each(function (index, article) {
  167. const top = $(article).offset().top - headerHeight;
  168. if (isVisible(article) && top > $(window).scrollTop() + 1) {
  169. scrollTo(top);
  170. currentArticle = article;
  171. return false;
  172. }
  173. });
  174. }
  175. break;
  176. case 75: // K - Scroll up
  177. if (seeAll && $(currentArticle).find('[aria-label="Go Back"]').length) {
  178. $(currentArticle).find('[aria-label="Go Back"]').click();
  179. } else {
  180. let previousArticle = null;
  181. $('article').each(function (index, article) {
  182. const top = $(article).offset().top - headerHeight;
  183. if (
  184. isVisible(article) &&
  185. top > $(window).scrollTop() - headerHeight - 20
  186. ) {
  187. if (previousArticle) {
  188. scrollTo($(previousArticle).offset().top - headerHeight);
  189. currentArticle = previousArticle;
  190. }
  191. return false;
  192. }
  193. previousArticle = article;
  194. });
  195. }
  196. break;
  197. case 76: // L - Like
  198. $('[aria-label="Like"],[aria-label="Unlike"]', currentArticle)
  199. .parent()
  200. .click();
  201. break;
  202. case 79: // O - Save
  203. const firstElement = $(
  204. '[aria-label="Save"],[aria-label="Remove"]',
  205. currentArticle
  206. )[0];
  207. $(firstElement).parent().click();
  208. break;
  209. case 32: // Space - Play/pause video
  210. findAndControlVideo('playPause');
  211. e.preventDefault(); // Prevent page scroll
  212. break;
  213. case 85: // U - Rewind
  214. findAndControlVideo('rewind');
  215. break;
  216. case 73: // I - Fast forward
  217. findAndControlVideo('fastForward');
  218. break;
  219. case 77: // M - Mute/unmute video
  220. findAndControlVideo('muteUnmute');
  221. break;
  222. }
  223. }
  224.  
  225. function postKeyboardShortcuts(e) {
  226. switch (e.keyCode) {
  227. case 74: // J - Next
  228. findAndClickButton('Next');
  229. break;
  230. case 75: // K - Previous
  231. findAndClickButton('Go back');
  232. break;
  233. case 32: // Space - Toggle slideshow
  234. e.preventDefault();
  235. if (isSlideshow) {
  236. stopSlideshow();
  237. } else {
  238. startSlideshow();
  239. }
  240. break;
  241. }
  242. }
  243.  
  244. // Keyboard shortcuts for Reels page
  245. function reelsKeyboardShortcuts(e) {
  246. switch (e.keyCode) {
  247. case 39: // Right arrow - Fast forward
  248. const videoFF = findTopVideo();
  249. if (videoFF) {
  250. videoFF.currentTime += fastForward;
  251. }
  252. e.preventDefault();
  253. break;
  254. case 37: // Left arrow - Rewind
  255. const videoRW = findTopVideo();
  256. if (videoRW) {
  257. videoRW.currentTime -= rewind;
  258. }
  259. e.preventDefault();
  260. break;
  261. }
  262. }
  263.  
  264. // Main keydown event handler
  265. $('body').keydown(function (e) {
  266. if (searchFocused || scrolling) return;
  267.  
  268. const currentPage = getCurrentPage();
  269. console.log('Current page:', currentPage);
  270.  
  271. switch (currentPage) {
  272. case 'home':
  273. homeKeyboardShortcuts(e);
  274. break;
  275. case 'reels':
  276. reelsKeyboardShortcuts(e);
  277. break;
  278. case 'post':
  279. postKeyboardShortcuts(e);
  280. break;
  281. }
  282. });
  283. });