Skip to content

Instantly share code, notes, and snippets.

@Meshiest
Created December 23, 2020 14:55
Show Gist options
  • Save Meshiest/1106f0ce85bf1982f3306417a54ce53d to your computer and use it in GitHub Desktop.
Save Meshiest/1106f0ce85bf1982f3306417a54ce53d to your computer and use it in GitHub Desktop.
generate brickadia gradient avatars with ease
<!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