Skip to content

Instantly share code, notes, and snippets.

@meodai
Created January 5, 2024 14:22
Show Gist options
  • Save meodai/40c0946e6a0825efd4a5f5fd57730feb to your computer and use it in GitHub Desktop.
Save meodai/40c0946e6a0825efd4a5f5fd57730feb to your computer and use it in GitHub Desktop.
import './style.css'
import { converter, trilerp, formatCss, easingSmoothstep } from 'culori';
const rgb = converter('rgb');
const RYB_CUBE = [ // custom RGB cube for RYB color space
{ mode: 'rgb', r: 231/255, g: 231/255, b: 232/255 }, // white point (paper background)
{
"mode": "rgb",
"r": 0.9,
"g": 0.1411764705882353,
"b": 0.12941176470588237
}, // red
{
"mode": "rgb",
"r": 0.9529411764705882,
"g": 0.9019607843137255,
"b": 0
}, // yellow
{
"mode": "rgb",
"r": 0.9411764705882353,
"g": 0.5568627450980392,
"b": 0.10980392156862745
}, // orange
{
"mode": "rgb",
"r": 0.08627450980392157,
"g": .8,
"b": 1
}, // blue
{
"mode": "rgb",
"r": 0.47058823529411764,
"g": 0.13333333333333333,
"b": 0.6666666666666666
}, // violet
{
"mode": "rgb",
"r": 0,
"g": 0.5568627450980392,
"b": 0.3568627450980392
}, // green
{ mode: 'rgb', r: 42/255, g: 40/255, b: 37/255 }, // black point (darkest color)
];
function ryb2rgb(coords) {
const r = easingSmoothstep(coords[0]);
const y = easingSmoothstep(coords[1]);
const b = easingSmoothstep(coords[2]);
return {
mode: 'rgb',
r: trilerp(...RYB_CUBE.map(it => it.r), r,y,b),
g: trilerp(...RYB_CUBE.map(it => it.g), r,y,b),
b: trilerp(...RYB_CUBE.map(it => it.b), r,y,b)
};
}
function hsl2farbrad (h, s, l) {
const rgbColor = rgb({
mode: 'hsl',
h: (h + 360) % 360, s, l: 1 - l
});
return ryb2rgb([
rgbColor.r,
rgbColor.g,
rgbColor.b
]);
};
const $can = document.querySelector('canvas');
const ctx = $can.getContext('2d');
let t = 0;
const a4paperRatio = 210 / 297;
const colorsToMix = [
{ // black
h: 0,
s: 0,
l: 0
},
{ // blue
h: 260,
s: 1,
l: 0.45
},
{ // teal
h: 216,
s: 1,
l: 0.3
},
{ // green
h: 170,
s: 1,
l: 0.6
},
{ // aqua
h: 240,
s: 1,
l: 0.7
},
{ // orange
h: 50,
s: 1,
l: 0.6
},
{ // pink
h: 330,
s: .8,
l: 0.7
},
{ // red
h: 6,
s: 1,
l: 0.5
},
{ // yellow
h: 120,
s: 1,
l: .5
},
].reverse();
function doIt () {
const w = window.innerWidth;
const h = window.innerHeight;
const paperH = h * .9;
const paperW = paperH * a4paperRatio;
const top = (h - paperH) / 2;
const left = (w - paperW) / 2;
let sampleSize = Math.min(w, h) * .1;
$can.width = w;
$can.height = h;
// draw paper background
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, w, h);
ctx.fillStyle = formatCss(hsl2farbrad(0, 0, 1));
ctx.fillStyle = "#ecebe7";
ctx.fillRect(left, top, paperW, paperH);
for (let x = 0; x < colorsToMix.length; x++) {
for (let y = 0; y < colorsToMix.length; y++) {
const color = colorsToMix[colorsToMix.length - 1 - x];
const color2 = colorsToMix[y];
const colorMix = hsl2farbrad(
(color.h + color2.h) / 2,
(color.s + color2.s) / 2,
(color.l + color2.l) / 2
);
// draw a transparent rectangle with a black border
ctx.lineWidth = .05;
ctx.strokeStyle = '#000';
ctx.strokeRect(
left + paperW * x / colorsToMix.length,
top + paperH * y / colorsToMix.length,
paperW / colorsToMix.length,
paperH / colorsToMix.length
);
let color1CSS = formatCss(hsl2farbrad(color.h, color.s, color.l));
let color2CSS = formatCss(hsl2farbrad(color2.h, color2.s, color2.l));
if (color1CSS === color2CSS) {
// set blending mode
ctx.globalCompositeOperation = 'source-over';
} else {
ctx.globalCompositeOperation = 'multiply';
}
ctx.fillStyle = color1CSS;
ctx.beginPath();
ctx.arc(
left + paperW * (x + 0.5) / colorsToMix.length,
top + paperH * (y + 0.5) / colorsToMix.length - sampleSize / 12,
sampleSize / 6,
0,
Math.PI * 2
);
ctx.fill();
ctx.fillStyle = color2CSS;
ctx.beginPath();
ctx.arc(
left + paperW * (x + 0.5) / colorsToMix.length,
top + paperH * (y + 0.5) / colorsToMix.length + sampleSize / 12,
sampleSize / 6,
0,
Math.PI * 2
);
ctx.fill();
// draw the intersection of the two colors
ctx.fillStyle = formatCss(colorMix);
}
}
}
doIt();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment