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