Skip to content

Instantly share code, notes, and snippets.

@meodai
Created July 2, 2023 17:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save meodai/8b82a15eb55fcc0d2c238f73d23d0e57 to your computer and use it in GitHub Desktop.
Save meodai/8b82a15eb55fcc0d2c238f73d23d0e57 to your computer and use it in GitHub Desktop.
vanilla JS color-scale generator (supports p3/rec2020)
<div class="world"></div>
const hardStopsGradient = (arrOfColors) => {
const l = arrOfColors.length;
return arrOfColors.map(
(c, i) => `${c} ${i/l*100}% ${(i+1)/l*100}%`
).join(',')
}
const scaleSpreadArray = (
initial,
targetSize,
fillFunction = (percent, lastValue, nextValue) => lastValue + percent * (nextValue - lastValue)
) => {
const valuesToAdd = targetSize - initial.length;
const chunkArray = initial.map((value) => [value]);
for (let i = 0; i < valuesToAdd; i++) {
chunkArray[i % (initial.length - 1)].push(null);
}
for (let i = 0; i < chunkArray.length - 1; i++) {
const currentChunk = chunkArray[i];
const nextChunk = chunkArray[i + 1];
const currentValue = currentChunk[0];
const nextValue = nextChunk[0];
for (let j = 1; j < currentChunk.length; j++) {
const percent = j / currentChunk.length;
currentChunk[j] = fillFunction(percent, currentValue, nextValue);
}
}
return chunkArray.flat();
};
const hxxToCSSokLCH = ({hue, chroma, lightness} = hxx) => `oklch(${lightness * 100}% ${chroma * .4} ${hue})`;
const generateHxxRamp = (
colors = 4,
minHueDiffAngle = 60,
) => {
minHueDiffAngle = Math.min(minHueDiffAngle, 360/colors);
const baseHue = Math.random() * 360;
const huesToPickFrom = new Array(
Math.round(360 / minHueDiffAngle)
).fill('').map((_, i) =>
(baseHue + i * minHueDiffAngle) % 360
);
while (huesToPickFrom.length > colors) {
const randomIndex = Math.floor(Math.random() * huesToPickFrom.length);
huesToPickFrom.splice(randomIndex, 1);
}
const minLightness = Math.random() * .2;
const lightnessRange = .9 - minLightness;
const minChroma = .5 + Math.random() * .2;
const maxChroma = minChroma + .3;
const maxLightness = Math.min(minLightness + .2 + Math.random() * .2, .95);
return huesToPickFrom.map((hue, i) => ({
lightness: minLightness + Math.random() * .1 + (lightnessRange/(colors-1)) * i,
chroma: minChroma + Math.random() * (maxChroma - minChroma),
hue,
}));
}
const generateColors = (
colorsToGenerate,
colorsFinal,
minHueDiffAngle = 60,
minIn = `oklch shorter hue`,
) => {
const baseColors = generateHxxRamp(colorsToGenerate, minHueDiffAngle);
const cssColorStops = baseColors.map(hxxToCSSokLCH);
return colorsFinal > colorsToGenerate ?
scaleSpreadArray(
cssColorStops,
colorsFinal,
(percent, lastValue, nextValue) =>
`color-mix(in ${minIn}, ${nextValue} ${percent * 100}%, ${lastValue})`
) : cssColorStops;
}
const $w = document.documentElement;
let doit = () => {
const gradient = generateColors(
Math.random() < .5 ? 2 : 4,
7
);
$w.style.setProperty('--bg', hardStopsGradient(gradient));
$w.style.setProperty('--bgs', gradient.join());
}
document.documentElement.addEventListener('click', doit);
doit();
.world {
--angle: 0deg;
background: linear-gradient(var(--angle), var(--bg));
height: 80vmin;
width: 60vmin;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
&::before {
content: '';
position: absolute;
inset: 0;
background: inherit;
filter: blur(6vmin);
transform: scale(.95);
}
}
@media (orientation: landscape) {
.world {
--angle: 90deg;
height: 35vmin;
width: 80vmin;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment