Created
December 23, 2020 14:55
-
-
Save Meshiest/1106f0ce85bf1982f3306417a54ce53d to your computer and use it in GitHub Desktop.
generate brickadia gradient avatars with ease
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
| <!doctype html> | |
| <style> | |
| #palette { | |
| display: inline-flex; | |
| width: auto; | |
| border: 1px solid black; | |
| } | |
| #palette > div { | |
| width: 30px; | |
| height: 30px; | |
| } | |
| </style> | |
| <p> | |
| <input id="startColor" value="#ffffff"> | |
| <input id="endColor" value="#000000"> | |
| </p> | |
| <p> | |
| <div id="palette"></div> | |
| </p> | |
| <input id="filename" value="avatar"><button id="downloadBtn">download</button> | |
| <a id="download" style="display: none"></a> | |
| <script> | |
| const avatar = { | |
| "formatVersion": "1", | |
| "presetVersion": "1", | |
| "type": "Avatar", | |
| "data": | |
| { | |
| "parts": [ | |
| { | |
| "partDescriptor": "PPD_Default_Pelvis", | |
| "decalDescriptor": "", | |
| "treeIndex": 0, | |
| "colors": [ | |
| { | |
| "b": 255, | |
| "g": 255, | |
| "r": 255, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| } | |
| ] | |
| }, | |
| { | |
| "partDescriptor": "PPD_Skeleton_Torso", | |
| "decalDescriptor": "PDD_Torso_Suit", | |
| "treeIndex": 1, | |
| "colors": [ | |
| { | |
| "b": 193, | |
| "g": 193, | |
| "r": 193, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 73, | |
| "g": 73, | |
| "r": 73, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 130, | |
| "g": 130, | |
| "r": 130, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 103, | |
| "g": 103, | |
| "r": 103, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 130, | |
| "g": 130, | |
| "r": 130, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 160, | |
| "g": 160, | |
| "r": 160, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 193, | |
| "g": 193, | |
| "r": 193, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 255, | |
| "g": 255, | |
| "r": 255, | |
| "a": 255 | |
| } | |
| ] | |
| }, | |
| { | |
| "partDescriptor": "PPD_Default_Head", | |
| "decalDescriptor": "PDD_Generic_Blank", | |
| "treeIndex": 2, | |
| "colors": [ | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 42, | |
| "g": 42, | |
| "r": 42, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| } | |
| ] | |
| }, | |
| { | |
| "partDescriptor": "PPD_Skeleton_Arm", | |
| "decalDescriptor": "", | |
| "treeIndex": 4, | |
| "colors": [ | |
| { | |
| "b": 103, | |
| "g": 103, | |
| "r": 103, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| } | |
| ] | |
| }, | |
| { | |
| "partDescriptor": "PPD_Cyborg_Wrist", | |
| "decalDescriptor": "", | |
| "treeIndex": 5, | |
| "colors": [ | |
| { | |
| "b": 160, | |
| "g": 160, | |
| "r": 160, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 255, | |
| "g": 255, | |
| "r": 255, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 193, | |
| "g": 193, | |
| "r": 193, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 130, | |
| "g": 130, | |
| "r": 130, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 42, | |
| "g": 42, | |
| "r": 42, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| } | |
| ] | |
| }, | |
| { | |
| "partDescriptor": "PPD_Cyborg_Hand", | |
| "decalDescriptor": "", | |
| "treeIndex": 6, | |
| "colors": [ | |
| { | |
| "b": 255, | |
| "g": 255, | |
| "r": 255, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 193, | |
| "g": 193, | |
| "r": 193, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 103, | |
| "g": 103, | |
| "r": 103, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 160, | |
| "g": 160, | |
| "r": 160, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 160, | |
| "g": 160, | |
| "r": 160, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| } | |
| ] | |
| }, | |
| { | |
| "partDescriptor": "PPD_Skeleton_Arm", | |
| "decalDescriptor": "", | |
| "treeIndex": 7, | |
| "colors": [ | |
| { | |
| "b": 103, | |
| "g": 103, | |
| "r": 103, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| } | |
| ] | |
| }, | |
| { | |
| "partDescriptor": "PPD_Cyborg_Wrist", | |
| "decalDescriptor": "", | |
| "treeIndex": 8, | |
| "colors": [ | |
| { | |
| "b": 160, | |
| "g": 160, | |
| "r": 160, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 255, | |
| "g": 255, | |
| "r": 255, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 193, | |
| "g": 193, | |
| "r": 193, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 130, | |
| "g": 130, | |
| "r": 130, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 42, | |
| "g": 42, | |
| "r": 42, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| } | |
| ] | |
| }, | |
| { | |
| "partDescriptor": "PPD_Cyborg_Hand", | |
| "decalDescriptor": "", | |
| "treeIndex": 9, | |
| "colors": [ | |
| { | |
| "b": 255, | |
| "g": 255, | |
| "r": 255, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 193, | |
| "g": 193, | |
| "r": 193, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 103, | |
| "g": 103, | |
| "r": 103, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 160, | |
| "g": 160, | |
| "r": 160, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 160, | |
| "g": 160, | |
| "r": 160, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| } | |
| ] | |
| }, | |
| { | |
| "partDescriptor": "PPD_Striped_Leg", | |
| "decalDescriptor": "", | |
| "treeIndex": 10, | |
| "colors": [ | |
| { | |
| "b": 73, | |
| "g": 73, | |
| "r": 73, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 42, | |
| "g": 42, | |
| "r": 42, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 103, | |
| "g": 103, | |
| "r": 103, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 130, | |
| "g": 130, | |
| "r": 130, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 160, | |
| "g": 160, | |
| "r": 160, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| } | |
| ] | |
| }, | |
| { | |
| "partDescriptor": "PPD_Striped_Boot", | |
| "decalDescriptor": "", | |
| "treeIndex": 11, | |
| "colors": [ | |
| { | |
| "b": 42, | |
| "g": 42, | |
| "r": 42, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 73, | |
| "g": 73, | |
| "r": 73, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| } | |
| ] | |
| }, | |
| { | |
| "partDescriptor": "PPD_Striped_Leg", | |
| "decalDescriptor": "", | |
| "treeIndex": 12, | |
| "colors": [ | |
| { | |
| "b": 73, | |
| "g": 73, | |
| "r": 73, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 42, | |
| "g": 42, | |
| "r": 42, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 103, | |
| "g": 103, | |
| "r": 103, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 130, | |
| "g": 130, | |
| "r": 130, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 160, | |
| "g": 160, | |
| "r": 160, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| } | |
| ] | |
| }, | |
| { | |
| "partDescriptor": "PPD_Striped_Boot", | |
| "decalDescriptor": "", | |
| "treeIndex": 13, | |
| "colors": [ | |
| { | |
| "b": 42, | |
| "g": 42, | |
| "r": 42, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 73, | |
| "g": 73, | |
| "r": 73, | |
| "a": 255 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| }, | |
| { | |
| "b": 0, | |
| "g": 0, | |
| "r": 0, | |
| "a": 0 | |
| } | |
| ] | |
| } | |
| ] | |
| } | |
| }; | |
| // in the future, it may be viable to order all the colors based on colorset order | |
| // so people can generate gradients for any avatar | |
| const ordered = Array.from(new Set( | |
| avatar.data.parts | |
| .flatMap(p => p.colors) | |
| .filter(c => c.a) | |
| .map(c => c.b) | |
| .sort((a, b) => a - b))); | |
| let colors = []; | |
| const $ = document.querySelector.bind(document); | |
| // regenerate the palette | |
| function recolor() { | |
| const isHex = s => !!s.match(/^#[0-9a-f]{6}$/i); | |
| const start = $('#startColor').value; | |
| const end = $('#endColor').value; | |
| if (!isHex(start) || !isHex(end)) return; | |
| const palette = $('#palette'); | |
| // empty the palette | |
| while (palette.firstChild) { | |
| palette.removeChild(palette.firstChild); | |
| } | |
| colors = []; | |
| // generate new colors in the palette | |
| for (let i = 0; i < ordered.length; i++) { | |
| // generate new color in gradient | |
| const color = lerpColor(start, end, i/(ordered.length-1)); | |
| colors.push(color); | |
| // load palette elements | |
| const el = document.createElement('div'); | |
| el.style.backgroundColor = color; | |
| palette.appendChild(el); | |
| } | |
| } | |
| // interoplate between two colors | |
| function lerpColor(a, b, amount) { | |
| // TODO: maybe change interp method + allow curve selection | |
| var ah = ah = +a.replace('#', '0x'), | |
| ar = ah >> 16, ag = ah >> 8 & 0xff, ab = ah & 0xff, | |
| bh = +b.replace('#', '0x'), | |
| br = bh >> 16, bg = bh >> 8 & 0xff, bb = bh & 0xff, | |
| rr = ar + amount * (br - ar), | |
| rg = ag + amount * (bg - ag), | |
| rb = ab + amount * (bb - ab); | |
| return '#' + ((1 << 24) + (rr << 16) + (rg << 8) + rb | 0).toString(16).slice(1); | |
| } | |
| function hexToRgba(hex) { | |
| const h = +hex.replace('#', '0x'); | |
| const r = h >> 16, g = h >> 8 & 0xff, b = h & 0xff; | |
| return { r, g, b, a: 255 }; | |
| } | |
| // save the avatar to a file | |
| function saveAvatar() { | |
| const a = $('#download'); | |
| // deep clone the original avatar | |
| const cloned = JSON.parse(JSON.stringify(avatar)); | |
| // color each of the parts (based on the blue index in ordered) | |
| for (const part of cloned.data.parts) { | |
| for (const color of part.colors) { | |
| if (color.a === 0) continue; | |
| const newColor = hexToRgba(colors[ordered.indexOf(color.b)]); | |
| Object.assign(color, newColor); | |
| } | |
| } | |
| // write to object url and save | |
| const json = JSON.stringify(cloned, 0, 2); | |
| const blob = new Blob([json], {type: "octet/stream"}); | |
| const url = window.URL.createObjectURL(blob); | |
| a.href = url; | |
| a.download = $('#filename').value + '.bp'; | |
| a.click(); | |
| window.URL.revokeObjectURL(url); | |
| } | |
| recolor(); | |
| $('#startColor').addEventListener('keyup', recolor); | |
| $('#endColor').addEventListener('keyup', recolor); | |
| $('#downloadBtn').addEventListener('click', saveAvatar); | |
| </script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment