Created
December 26, 2025 04:36
-
-
Save Razboy20/28ea01027b31f8d332bd24130517b18b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // note: this is extremely ugly patched together code from *years* ago | |
| // that wasn't meant to see the light of day. | |
| // But hey, I suppose it 'works'. | |
| // | |
| // Fun fact: this code was initially written for animating gradients for minecraft titles! | |
| // | |
| // https://www.youtube.com/watch?v=Cc2nkx77U24 | |
| function rgbToHex(r, g, b) { | |
| return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); | |
| } | |
| function hexToRgb(hex) { | |
| var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); | |
| return result ? [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)] : null; | |
| } | |
| /** | |
| * Interpolates between two hex colors based on a weight value. | |
| * Interpolation is linear, a better and more pleasant way would be to interpolate in HSL or OKLAB/OKHSL colorspaces. | |
| */ | |
| function pickHex(color1, color2, weight) { | |
| color1 = hexToRgb(color1); | |
| color2 = hexToRgb(color2); | |
| weight = 1 - weight; | |
| var w1 = weight; | |
| var w2 = 1 - w1; | |
| var rgb = [Math.round(color1[0] * w1 + color2[0] * w2), Math.round(color1[1] * w1 + color2[1] * w2), Math.round(color1[2] * w1 + color2[2] * w2)]; | |
| return rgbToHex(rgb[0], rgb[1], rgb[2]); | |
| } | |
| const colors = new Set(); | |
| /** | |
| * Given a gradient and a string, | |
| * interpolates the string's characters with colors from the gradient. | |
| */ | |
| function fullTextGradation(gradient, text) { | |
| const result = []; | |
| for (const li in text) { | |
| const l = text[li]; | |
| let position = Math.round((1 / (text.length - 1)) * parseInt(li) * 100000) / 100000; | |
| let colorRange; | |
| for (let index in gradient) { | |
| index = parseInt(index); | |
| const value = gradient[index]; | |
| if (index == gradient.length - 1 && position > value[0]) position = value[0]; | |
| if (position <= value[0]) { | |
| if (index == 0) index++; | |
| colorRange = [index - 1, index, (position - gradient[index - 1][0]) / (gradient[index][0] - gradient[index - 1][0])]; | |
| break; | |
| } | |
| } | |
| const color = pickHex(gradient[colorRange[0]][1], gradient[colorRange[1]][1], colorRange[2]); | |
| colors.add(color); | |
| // add colored text using webvtt syntax to result | |
| result.push(`<c.color${color.toUpperCase().slice(1)}>${l}</c>`); | |
| } | |
| return result.join(""); | |
| } | |
| // why is the gradient repeated 3 times? Because I was lazy. | |
| var rainbow = [ | |
| [-2, rgbToHex(255, 0, 0)], | |
| [-1.9, rgbToHex(255, 154, 0)], | |
| [-1.8, rgbToHex(208, 222, 33)], | |
| [-1.7, rgbToHex(79, 220, 74)], | |
| [-1.6, rgbToHex(63, 218, 216)], | |
| [-1.5, rgbToHex(47, 201, 226)], | |
| [-1.4, rgbToHex(28, 127, 231)], | |
| [-1.3, rgbToHex(95, 21, 242)], | |
| [-1.2, rgbToHex(186, 12, 248)], | |
| [-1.1, rgbToHex(251, 7, 217)], | |
| [-1, rgbToHex(255, 0, 0)], | |
| [-0.9, rgbToHex(255, 154, 0)], | |
| [-0.8, rgbToHex(208, 222, 33)], | |
| [-0.7, rgbToHex(79, 220, 74)], | |
| [-0.6, rgbToHex(63, 218, 216)], | |
| [-0.5, rgbToHex(47, 201, 226)], | |
| [-0.4, rgbToHex(28, 127, 231)], | |
| [-0.3, rgbToHex(95, 21, 242)], | |
| [-0.2, rgbToHex(186, 12, 248)], | |
| [-0.1, rgbToHex(251, 7, 217)], | |
| [0, rgbToHex(255, 0, 0)], | |
| [0.1, rgbToHex(255, 154, 0)], | |
| [0.2, rgbToHex(208, 222, 33)], | |
| [0.3, rgbToHex(79, 220, 74)], | |
| [0.4, rgbToHex(63, 218, 216)], | |
| [0.5, rgbToHex(47, 201, 226)], | |
| [0.6, rgbToHex(28, 127, 231)], | |
| [0.7, rgbToHex(95, 21, 242)], | |
| [0.8, rgbToHex(186, 12, 248)], | |
| [0.9, rgbToHex(251, 7, 217)], | |
| [1, rgbToHex(255, 0, 0)] | |
| ]; | |
| // --- | |
| // slapped on code to output webvtt | |
| let output = ""; | |
| for (let i = 0; i < 80; i++) { | |
| // create time code for subtitle relative to 0 based on 'i' for webvtt captions | |
| // create webvtt time code where time is in milliseconds, and padd the seconds with 2 0's | |
| function generateTimecode(time) { | |
| const timecode = `00:00:${Math.floor(time / 1000) | |
| .toString() | |
| .padStart(2, "0")}.${Math.floor(time % 1000) | |
| .toString() | |
| .padStart(3, "0")}`; | |
| return timecode; | |
| } | |
| const text = fullTextGradation( | |
| rainbow.map(g => [g[0] + (i / 79) * 2, g[1]]), | |
| "Rainbows are amazing!" | |
| ); | |
| output += `${generateTimecode((i * 1000) / 10)} --> ${generateTimecode(((i + 1) * 1000) / 10)}\n${text}\n\n`; | |
| } | |
| let output_header = "WEBVTT\nStyle:\n"; | |
| // add cue styles based on colors | |
| for (let color of colors) { | |
| color = color.toUpperCase().slice(1); | |
| output_header += `::cue(c.color${color}) { color: #${color};\n}\n`; | |
| } | |
| output = output_header + "##\n\n" + output; | |
| // write 'output' to 'subtitlegradients.vtt' | |
| import { writeFileSync } from "fs"; | |
| writeFileSync("subtitlegradients.vtt", output); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment