Skip to content

Instantly share code, notes, and snippets.

@harunpehlivan
Created July 22, 2021 09:56
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 harunpehlivan/178682f1dac7b834bfe8f4109aacd1a6 to your computer and use it in GitHub Desktop.
Save harunpehlivan/178682f1dac7b834bfe8f4109aacd1a6 to your computer and use it in GitHub Desktop.
color ramp generator
<article>
<h2>HSL Slice Preview</h2>
<figure>
<svg data-figure viewbox="0 0 100 100">
</svg>
</figure>
<h2>Full Palette</h2>
<div data-colors></div>
<h2>
Example Use: <strong>4 random colors form palette</strong>
(click to re-generate)
</h2>
<div data-palette>
<b>
<i></i>
<i></i>
</b>
</div>
<div data-ramp></div>
</article>
// https://medium.com/@greggunn/how-to-make-your-own-color-palettes-712959fbf021
const hsv2hsl = (h,s,v,l=v-v*s/2, m=Math.min(l,1-l)) => [h,m?(v-l)/m:0,l];
const random = (min, max) => {
if (!max) {
max = min;
min = 0;
}
return Math.random() * (max - min) + min
};
const shuffleArray = array => {
let arr = [...array];
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}
const generateRandomColorRamp = (
total,
baseColorH = random(360),
hueCycle = .3,
offset = 0.05,
curveMod = 0,
hueShift = .1,
) => {
const baseColors = [];
const SL = [];
const part = (Math.PI/2) / (total + 1);
for (let i = 1; i < (total + 1); i++) {
/*
-(Math.sin(-Math.PI/2 + i * part - curveMod) + offset),
1 - Math.cos(-Math.PI/2 + i * part + curveMod) - offset
*/
/*
let x = Math.cos(-Math.PI/2 + i * part + curveMod) - offset;
let y = Math.sin(-Math.PI/2 + i * part - curveMod) + offset;
let hsl = hsv2hsl(0,x,-y);
*/
/*
let hsl = hsv2hsl(
(baseColorH + i * (360/total) * hueCycle) % 360,
(Math.cos(-Math.PI/2 + i * part + curveMod)),
(1 + Math.sin(-Math.PI/2 + i * part - curveMod))
)
*/
let hsl = hsv2hsl(
(baseColorH + i * (360/total) * hueCycle) % 360,
Math.cos(-Math.PI/2 + i * part + curveMod),
-Math.sin(-Math.PI/2 + i * part - curveMod)
)
baseColors.push(
[
hsl[0],
hsl[1],
hsl[2]
]
)
/*
(offset + Math.cos(-Math.PI/2 + i * part + curveMod)),
(offset + 1 + Math.sin(-Math.PI/2 + i * part - curveMod))
*/
}
const lightColors = baseColors.map(c => [(c[0] + 360 * hueShift) % 360, c[1] - offset, c[2] + offset])
const darkColors = baseColors.map(c => [(c[0] - 360 * hueShift) % 360, c[1] - offset, c[2] - offset])
return [...lightColors,...baseColors,...darkColors];
}
const pane = new Tweakpane.Pane();
const PARAMS = {
colors: 9,
baseColorH: 280,
hueCycle: .4,
offset: 0.1,
curveMod: 0,
hueShift: .02,
};
// `min` and `max`: slider
pane.addInput(
PARAMS, 'colors',
{min: 3, max: 15, step: 1}
);
pane.addInput(
PARAMS, 'baseColorH',
{min: 0, max: 360, step: 0.1}
);
pane.addInput(
PARAMS, 'hueCycle',
{min: 0, max: 1.5, step: 0.001}
);
pane.addInput(
PARAMS, 'offset',
{min: 0, max: 0.4, step: 0.001}
);
pane.addInput(
PARAMS, 'curveMod',
{min: 0, max: 1, step: 0.001}
);
pane.addInput(
PARAMS, 'hueShift',
{min: 0, max: 1, step: 0.001}
);
const $pal = document.querySelector('[data-palette]');
const $picker = document.querySelector('[data-figure]');
const $ramp = document.querySelector('[data-ramp]');
let colors = [];
const palette = (colors, method) => {
let allColors = [];
const lightColors = shuffleArray( colors.slice(0, colors.length/3 - 1) );
const mediumColors = shuffleArray( colors.slice(colors.length/3 - 1, (colors.length/3) * 2 - 1) );
const darkColors = shuffleArray( colors.slice((colors.length/3) * 2 - 1, colors.length - 1) );
switch (method) {
case 'random':
allColors = shuffleArray(colors);
break;
case 'l2md':
allColors = [lightColors[0], mediumColors[0], darkColors[0], lightColors[1]];
break;
case 'lmd2':
allColors = [darkColors[0], mediumColors[0], lightColors[0], darkColors[1]];
break;
case 'lm2d':
allColors = [mediumColors[1], mediumColors[0], lightColors[0], darkColors[1]];
break;
}
for(let i = 0; i < 4; i++) {
$pal.style.setProperty(`--col-${i}`, `hsl(${allColors[i][0]},${allColors[i][1] * 100}%,${allColors[i][2] * 100}%)`);
}
$ramp.style.setProperty('background', `linear-gradient(90deg, ${allColors.slice(0,4).map(c => `hsl(${c[0]},${c[1] * 100}%,${c[2] * 100}%)`).join(',')})`)
}
function bam() {
colors = generateRandomColorRamp(
PARAMS.colors,
PARAMS.baseColorH,
PARAMS.hueCycle,
PARAMS.offset,
PARAMS.curveMod,
PARAMS.hueShift,
);
points(PARAMS.colors, PARAMS.offset, PARAMS.curveMod, colors.slice(colors.length/3, (colors.length/3) * 2));
/*
let shuffledcolors = colors
.map((a) => ({sort: Math.random(), value: a}))
.sort((a, b) => a.sort - b.sort)
.map((a) => a.value)*/
$picker.style.setProperty(`--deg`, `${colors[Math.floor(colors.length * .5)][0]}deg`);
document.querySelector('[data-colors]').innerHTML = colors.reduce((r,c) => `${r}<i style="--w: ${1/PARAMS.colors}; --h: ${c[0]}; --s: ${c[1] * 100}; --l: ${c[2] * 100}"></i>`,'');
palette(colors, 'random');
}
function points (colorsInt, offset, curveMod, colorsArr) {
$picker.innerHTML = '';
const part = (Math.PI/2) / (colorsInt + 1);
for (let i = 1; i < (colorsInt + 1); i++) {
let x = Math.cos(-Math.PI/2 + i * part + curveMod) - offset;
let y = Math.sin(-Math.PI/2 + i * part - curveMod) + offset;
let hsl = hsv2hsl(0,x,-y);
let newElement = document.createElementNS("http://www.w3.org/2000/svg", 'circle');
newElement.setAttribute('cx', x * 100);
newElement.setAttribute('cy', 100 + y * 100);
newElement.setAttribute('r','3');
newElement.style.fill = `hsl(${colorsArr[i-1][0]}deg, ${hsl[1] * 100}%,${hsl[2] * 100}%)`; //Set stroke colour
newElement.style.stroke = '#212121'; //Set stroke colour
newElement.style.strokeWidth = '.5px'; //Set stroke width
$picker.appendChild(newElement);
}
}
$pal.addEventListener('click', () => palette(colors, 'random'));
pane.on('change', bam);
bam();
<script src="https://cdn.jsdelivr.net/npm/tweakpane@3.0.3/dist/tweakpane.min.js"></script>
@import url('https://rsms.me/inter/inter.css');
:root {
font-family: 'Inter', sans-serif;
background: #212121;
}
article {
width: 25rem;
padding: 2rem;
background: #212121;
position: absolute;
top: 0;
bottom: 0;
overflow: auto;
//display: flex;
//flex-direction: column;
}
h2 {
margin: 2rem 0 1rem;
color: #fff;
&:first-child {
margin-top: 0;
}
}
[data-colors] {
display: flex;
flex-wrap: wrap;
width: 100%;
}
[data-colors] i {
flex: 1 0 calc(var(--w, 0.11) * 100%);
width: calc(var(--w, 0.11) * 100%);
padding-top: calc(var(--w, 0.11) * 100%);
background: hsl(var(--h), calc(var(--s) * 1%), calc(var(--l) * 1%))
}
[data-palette] {
position: relative;
background: var(--col-0);
padding-top: 100%;
margin: 2rem 0;
b {
position: absolute;
top: 50%;
left: 50%;
width: 50%;
height: 50%;
transform: translate(-50%,-50%);
background: var(--col-1);
}
i {
position: absolute;
width: 50%;
height: 50%;
right: 0;
}
i:first-child {
background: var(--col-2);
}
i:last-child {
bottom: 0;
background: var(--col-3);
}
}
figure {
margin: 2rem 0;
padding: 0;
}
[data-figure] {
background-image: linear-gradient(to top, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0)),
linear-gradient(to left, hsl(var(--deg, 0deg), 100%, 50%), hsl(var(--deg, 0deg), 0%, 100%));
}
[data-ramp] {
height: 4rem;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment