Confetti Changer

Hijack submitty's confetti with our own colors (and more features soon™)

  1. // ==UserScript==
  2. // @name Confetti Changer
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.3
  5. // @description Hijack submitty's confetti with our own colors (and more features soon™)
  6. // @author Colin SF
  7. // @match https://submit.scss.tcd.ie/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=tcd.ie
  9. // @grant none
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. /* eslint-disable no-multi-spaces */ // so the linter doesn't complain about my formatting :)
  14.  
  15. // to test or preview these hex colors you can use https://www.w3schools.com/colors/colors_hexadecimal.asp or https://www.color-hex.com/
  16. // color themes can be added just by adding a new const list on a new line
  17.  
  18. const JANUARY_COLORS = ['#406BC9','#FFFFFF','#809BCE','#9AC8DE','#B6C7BE'];
  19. const FEBRUARY_COLORS = ['#DF3B57','#EE4B6A','#7D2335','#86CEC5','#B2E6F1'];
  20. const MARCH_COLORS = ['#8DB62F','#7B9233','#034121','#022607','#FFCC00'];
  21. const APRIL_COLORS = ['#EED149','#3BCA8B','#9EE0E7','#EBB8AA','#FFFFFF'];
  22. const MAY_COLORS = ['#F9EAE5','#F16878','#C1DBB3','#7EBC89','#FF8154'];
  23. const JUNE_COLORS = ['#EC4067','#F4D35E','#F78764','#00889F','#083D77'];
  24. const JULY_COLORS = ['#FFFFFF','#DE1A1A','#090C9B'];
  25. const AUGUST_COLORS = ['#F0A202','#FF4040','#F2C940','#AB2321'];
  26. const SEPTEMBER_COLORS = ['#8FD7FF','#316498','#34CA34','#FFFF40','#FF2929','#9C84A4'];
  27. const OCTOBER_COLORS = ['#000000','#FF6700','#291528'];
  28. const NOVEMBER_COLORS = ['#5A351E','#522B47','#912F09','#F0A202','#FBF5F3'];
  29. const DECEMBER_COLORS = ['#D7CDCC','#F7B11D','#1F5E00','#DE1A1A','#FFFFFF'];
  30. const DEFAULT_COLORS = ['#8FD7FF','#316498','#34CA34','#FFFF40','#FF2929','#9C84A4'];
  31. const MARTINI = ['#BB0059','#FF1493','#00BFFF','#FFF0F5','#FF69B4','#ADD8E6']; // sweet girl
  32. const USER_COLORS = ['#FFFFFF','#808080','#000000']; // YOUR COLORS HERE!
  33.  
  34. // CONFETTI PARAMETERS
  35. var CONFETTI_COLORS = MARTINI; // Set color theme here, can change to any const above or "PER_MONTH" if you want submitty's naturally updating confetti
  36. const CONFETTI_DENSITY = 0.75; // must be a float between [0,inf), 1.0 is submitty's original behavior (but large values will probably tank performance)
  37.  
  38. // handle per-month confetti color choice
  39. if ( CONFETTI_COLORS == "PER_MONTH" ) {
  40. let month = new Date().getMonth();
  41. CONFETTI_COLORS = [
  42. JANUARY_COLORS,
  43. FEBRUARY_COLORS,
  44. MARCH_COLORS,
  45. APRIL_COLORS,
  46. MAY_COLORS,
  47. JUNE_COLORS,
  48. JULY_COLORS,
  49. AUGUST_COLORS,
  50. SEPTEMBER_COLORS,
  51. OCTOBER_COLORS,
  52. NOVEMBER_COLORS,
  53. DECEMBER_COLORS
  54. ][month];
  55. }
  56.  
  57. // copy the page's addConfetti() func, alter it so it always uses whatever colors we want
  58. // and then substitute it for the window's original function to customize our confetti
  59. // ( NB: you should never have to go in here unless you're curious :) )
  60. function addConfetti() {
  61. const canvas = document.getElementById('confetti_canvas');
  62. if (!canvas) {
  63. return;
  64. }
  65. let times_ran = 0;
  66. let frame = 0;
  67.  
  68. //destroy the canvas animation on click or on enter
  69. canvas.addEventListener('click', () => {
  70. if (canvas.style.display !== 'none') {
  71. canvas.style.display = 'none';
  72. return;
  73. }
  74. });
  75.  
  76. window.addEventListener('keypress', (e) => {
  77. if (e.code === 'Enter' && canvas.style.display !== 'none') {
  78. canvas.style.display = 'none';
  79. return;
  80. }
  81. if (e.key === 'p' || e.key === 'P'){
  82. confettiPaused = !confettiPaused;
  83. }
  84. });
  85.  
  86. // Resume confetti animation when window is visible again
  87. window.addEventListener('visibilitychange', () => {
  88. if (document.visibilityState === 'visible' && is_drawing) {
  89. lastUpdateTime = Date.now();
  90. draw();
  91. }
  92. });
  93.  
  94. canvas.width = window.innerWidth;
  95. const body = document.body;
  96. const html = document.documentElement;
  97. canvas.height = Math.max( body.scrollHeight, body.offsetHeight,
  98. html.clientHeight, html.scrollHeight, html.offsetHeight );
  99.  
  100. canvas.style.display = 'block';
  101.  
  102. const ctx = canvas.getContext('2d');
  103. const pieces = [];
  104. const numberOfPieces = 2500 * CONFETTI_DENSITY;
  105. let lastUpdateTime = Date.now();
  106. const x_const = 0.25;
  107. const max_times = 250;
  108. const size_const = 10;
  109. const gravity_const = 0.25;
  110.  
  111. let is_drawing = false;
  112.  
  113. function randomColor () {
  114. return CONFETTI_COLORS[Math.floor(Math.random() * CONFETTI_COLORS.length)];
  115. }
  116.  
  117. function update () {
  118. const now = Date.now(),
  119. dt = now - lastUpdateTime;
  120.  
  121. for (let i = pieces.length - 1; i >= 0; i--) {
  122. const p = pieces[i];
  123.  
  124. if (p.y > canvas.height) {
  125. pieces.splice(i, 1);
  126. continue;
  127. }
  128.  
  129. p.y += p.gravity * dt;
  130. p.rotation += p.rotationSpeed * dt;
  131. p.x += p.x_vel;
  132. }
  133.  
  134. while (pieces.length < numberOfPieces && times_ran < max_times) {
  135. pieces.push(new Piece(Math.random() * canvas.width, -20));
  136. }
  137.  
  138. lastUpdateTime = now;
  139.  
  140. times_ran ++;
  141.  
  142. if (times_ran >= max_times * 10) {
  143. ctx.clearRect(0, 0, canvas.width, canvas.height);
  144. canvas.style.display = '';
  145. canvas.width = 0;
  146. canvas.height = 0;
  147. }
  148.  
  149. }
  150.  
  151. function draw () {
  152.  
  153. if (canvas.style.display === 'none') {
  154. cancelAnimationFrame(frame);
  155. is_drawing = false;
  156. return;
  157. }
  158.  
  159. is_drawing = true;
  160.  
  161. // Stop the confetti animation if the page is not visible
  162. if (document.visibilityState === 'hidden') {
  163. cancelAnimationFrame(frame);
  164. return;
  165. }
  166.  
  167. update();
  168. ctx.clearRect(0, 0, canvas.width, canvas.height);
  169.  
  170. pieces.forEach((p) => {
  171. ctx.save();
  172.  
  173. ctx.fillStyle = p.color;
  174.  
  175. ctx.translate(p.x + p.size / 25, p.y + p.size / 2);
  176. ctx.rotate(p.rotation);
  177.  
  178. ctx.fillRect(-p.size / 2, -p.size / 2, p.size, p.size);
  179.  
  180. ctx.restore();
  181. });
  182.  
  183. frame = requestAnimationFrame(draw);
  184. }
  185.  
  186. function Piece (x, y) {
  187. this.x = x;
  188. this.y = y;
  189. this.x_vel = (Math.random() - 0.5) * x_const;
  190. this.size = (Math.random() * 0.5 + 0.75) * size_const;
  191. this.gravity = (Math.random() * 0.5 + 0.75) * gravity_const;
  192. this.rotation = (Math.PI * 2) * Math.random();
  193. this.rotationSpeed = (Math.PI * 2) * (Math.random() - 0.5) * 0.0015;
  194. this.color = randomColor();
  195. }
  196.  
  197. while (pieces.length < numberOfPieces) {
  198. pieces.push(new Piece(Math.random() * canvas.width, Math.random() * canvas.height));
  199. }
  200.  
  201. draw();
  202. }
  203.  
  204. (function() {
  205. 'use strict';
  206. window.addConfetti = addConfetti;
  207. })();