+var color = document.querySelector('.color'),
+ light = document.querySelector('.light'),
+ timeline = document.querySelector('.timeline'),
+ clear = document.querySelector('.clear'),
+ colors = [],
+ selecting = false,
+ recording = false,
+ animationTime = 0,
+ lastFrame = 0,
+ t = null,
+ h = 0,
+ s = 100,
+ l = 50;
+
+function render() {
+ color.style.backgroundColor = `hsl(${h} ${s}% ${l}%)`;
+ light.style.transform = `translateY(-${l*2.2}px)`;
+}
+
+function renderTimeline() {
+ var background = 'linear-gradient(90deg,',
+ last = 0;
+ for (var {h, s, l, t: now} of colors) {
+ background += `hsl(${h} ${s}% ${l}%) ${Math.round(100*last/3000)}%,`;
+ background += `hsl(${h} ${s}% ${l}%) ${Math.round(100*(last+now)/3000)}%,`;
+ last += now;
+ }
+ background += `black ${Math.round(100*last/3000)}%, black 101%)`;
+ timeline.style.background = background;
+}
+
+function length() {
+ return colors.map((c) => c.t).reduce((s, t) => s + t, 0);
+}
+
+function addColor() {
+ t = Date.now();
+ colors.push({h, s, l, t: 0});
+}
+
+function record() {
+ if (recording) setTimeout(record, 100);
+ var c = colors[colors.length - 1],
+ l = length();
+ if (c.h !== h || c.s !== s || c.l !== l) {
+ c.t = Math.min(3000, Date.now() - t);
+ addColor();
+ c = colors[colors.length - 1];
+ }
+ if (l >= 3000) return;
+ c.t = Math.min(3000, Date.now() - t);
+ renderTimeline();
+}
+
+function preview(current) {
+ if (selecting || recording) return;
+ window.requestAnimationFrame(preview);
+ var dt = current - lastFrame,
+ last = 0;
+ if (dt > 32) {
+ animationTime = (animationTime + dt) % 3000
+ color.style.background = `hsl(0 0% 0%)`;
+ for (var {h, s, l, t: now} of colors) {
+ if (animationTime >= last && animationTime < last+now) {
+ color.style.background = `hsl(${h} ${s}% ${l}%)`;
+ break;
+ }
+ last += now;
+ }
+ lastFrame = current;
+ }
+}
+
+color.addEventListener('mousemove', ({offsetX: x, offsetY: y}) => {
+ h = Math.round(360 * x / 200);
+ s = Math.round(100 * (200 - y) / 200);
+ render();
+});
+
+color.addEventListener('wheel', ({deltaY: y}) => {
+ selecting = true;
+ l = Math.min(Math.max(0, l + y/4), 100)
+ render();
+});
+
+color.addEventListener('mousedown', () => {
+ addColor();
+ recording = true;
+ setTimeout(record, 100);
+});
+
+color.addEventListener('mouseup', () => {
+ recording = false;
+});
+
+color.addEventListener('mouseenter', () => {
+ selecting = true;
+});
+
+color.addEventListener('mouseout', () => {
+ recording = false;
+ selecting = false;
+ window.requestAnimationFrame(preview);
+});
+
+clear.addEventListener('click', () => {
+ colors = [];
+ renderTimeline();
+});
+
+render();
+renderTimeline();