createCanvas(windowWidth, windowHeight);
colorMode(HSB, 360, 100, 100);
textAlign(CENTER, CENTER);
const palletHeight = 100;
const ww = (width-((colors.length+2)*space))/colors.length;
for(let i = 0; i < colors.length; i++){
fill(hue(col), saturation(col)*sat_factor, brightness(col));
rect(i*(ww+space) + space, space, ww, palletHeight-space*2);
text(words[i], i*(ww+space) + space + ww/2, palletHeight-space*2);
text(i, i*(ww+space) + space + ww/2, palletHeight / 2);
console.log(words[i], hsbToHex(hue(col), saturation(col)*sat_factor, brightness(col)));
let result = generateCombinations(colors, 3);
if(SHUFFLE) result = shuffle(result);
const cols = floor(sqrt(result.length));
const rows = ceil(result.length/cols);
const w = (width - (cols+2)*space) / cols;
const h = (height - space*2 - palletHeight - space*rows) / rows;
result.forEach((combination, i) => {
const gridY = Math.floor(i / cols);
if (gridY >= rows) return;
combination.forEach((col, j) => {
fill(hue(col), saturation(col)*sat_factor, brightness(col));
rect(gridX * w + w/3 * j + gridX * space + space, gridY * (h + space) + space + palletHeight, w/3, h);
function hsbToHex(hue, sat, bri) {
const i = Math.floor(h * 6);
const q = v * (1 - f * s);
const t = v * (1 - (1 - f) * s);
case 0: r = v; g = t; b = p; break;
case 1: r = q; g = v; b = p; break;
case 2: r = p; g = v; b = t; break;
case 3: r = p; g = q; b = v; break;
case 4: r = t; g = p; b = v; break;
case 5: r = v; g = p; b = q; break;
const toHex = (c) => c.toString(16).padStart(2, '0');
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
function generateCombinations(options, groupSize) {
function combine(current, start) {
if (current.length === groupSize) {
if(SHUFFLE) current = shuffle(current);
combinations.push([...current]);
for (let i = start; i < options.length; i++) {
current.push(options[i]);
p5.prototype.saturation = function(c) {
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
const saturation = max === 0 ? 0 : delta / max;