Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Auto Save

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                a.github-corner(href='//github.com/meodai/color-names', target='_blank', aria-label='View source on Github')
  svg(width='80', height='80', viewbox='0 0 250 250', aria-hidden='true')
    path.triange(d='M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z')
    path.octo-arm(d='M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2', style='transform-origin: 130px 106px;')
    path.octo-body(d='M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z')
.options
  label
    span.label List
    select(data-list)
      option(value="default") Color Name List
      option(value="bestOf") Best of
      option(value="short") Short Names
      option(value="basic") Basic Colors
      option(value="html") HTML / CSS
      option(value="wikipedia") Wikipedia
      option(value="french") French Colors
      option(value="spanish") Spanish Colors
      option(value="japaneseTraditional") Japanese Traditional
      option(value="chineseTraditional") Chinese Traditional
      option(value="leCorbusier") Le Corbusier
      option(value="nbsIscc") NBS-ISCC
      option(value="ntc") NTC.js
      option(value="osxcrayons") OSX Crayon
      option(value="ral") RAL color
      option(value="ridgway") Ridgway's Nomenclature 
      option(value="sanzoWadaI") Sanzo Wada
      option(value="thesaurus") Thesaurus
      option(value="werner") Werner's Nomenclature
      option(value="windows") MS Windows
      option(value="x11") x11
      option(value="xkcd") xkcd survey
      
  
  label
    span.label Model
    select(data-model)
      option(value="rgb") rgb
      option(value="xyz") xyz
      option(value="cat02") cat02
      option(value="jab") jab
      option(value="luv") luv
      option(value="yuv") yuv
      option(value="lab") lab
      option(value="oklab") OK lab
      option(value="lch") lch
      option(value="hsv") hsv
      option(value="hsl") hsl
      option(value="hsi") hsi
      option(value="hwb") hwb
      option(value="hcg") hcg
      
button 
  span toggle darkmode
  strong.
    <svg xmlns="http://www.w3.org/2000/svg" width="184" height="184" viewBox="0 0 184 184">
      <g>
        <path class="line" d="M43.27,54.49A7.93,7.93,0,0,0,54.49,43.27l-8.33-8.33A7.93,7.93,0,0,0,34.95,46.16Z" transform="translate(-8 -8)"/>
        <path class="line" d="M159.45,167.38a7.93,7.93,0,0,0,5.61-13.54l-8.33-8.33a7.93,7.93,0,0,0-11.21,11.21l8.33,8.33A7.9,7.9,0,0,0,159.45,167.38Z" transform="translate(-8 -8)"/>
        <path class="line" d="M46.16,165.05l8.33-8.33a7.93,7.93,0,1,0-11.21-11.21l-8.33,8.33a7.93,7.93,0,1,0,11.21,11.21Z" transform="translate(-8 -8)"/>
        <path class="line" d="M151.12,56.81a7.91,7.91,0,0,0,5.61-2.32l8.33-8.33a7.93,7.93,0,0,0-11.21-11.21l-8.33,8.33a7.93,7.93,0,0,0,5.61,13.54Z" transform="translate(-8 -8)"/>
        <path class="line" d="M15.93,107.93H27.71a7.93,7.93,0,0,0,0-15.86H15.93a7.93,7.93,0,0,0,0,15.86Z" transform="translate(-8 -8)"/>
        <path class="line" d="M164.36,100a7.93,7.93,0,0,0,7.93,7.93h11.78a7.93,7.93,0,0,0,0-15.86H172.29A7.93,7.93,0,0,0,164.36,100Z" transform="translate(-8 -8)"/>
        <path class="line" d="M100,164.36a7.93,7.93,0,0,0-7.93,7.93v11.78a7.93,7.93,0,0,0,15.86,0V172.29A7.93,7.93,0,0,0,100,164.36Z" transform="translate(-8 -8)"/>
        <path class="line" d="M100,35.64a7.93,7.93,0,0,0,7.93-7.93V15.93a7.93,7.93,0,0,0-15.86,0V27.71A7.93,7.93,0,0,0,100,35.64Z" transform="translate(-8 -8)"/>
        <path class="circle" d="M100,153.09A53.09,53.09,0,1,0,46.91,100,53.15,53.15,0,0,0,100,153.09Zm0-90.33A37.23,37.23,0,1,1,62.77,100,37.27,37.27,0,0,1,100,62.77Z" transform="translate(-8 -8)"/>
      </g>
    </svg>

.currentColor

#container

              
            
!

CSS

              
                $c-black: #202124;
$c-white: #fff;
$bg: $c-white;

:root {
  --dark: $c-black;
  --light: $c-white;
  --background: var($c-black);
  --foreground: var($c-white);
}

// <link href="https://fonts.googleapis.com/css?family=Inconsolata" rel="stylesheet">
//@import 'https://fonts.googleapis.com/css?family=Inconsolata';
//$t-code: 'Inconsolata', ipm, Menlo, 'Courier New', monospace;

@import url('https://fonts.googleapis.com/css2?family=Inter:wght@200&display=swap');

$t-code: 'Inter', sans-serif;
//@import 'https://fonts.googleapis.com/css?family=Space+Mono';
//$t-code: 'Space Mono', ipm, Menlo, 'Courier New', monospace;

body {
  overflow: hidden;
  background: $c-black;
  background: var(--background);
  font-family: $t-code;
  color: #fff;
  color: var(--foreground, #{$c-white});
}
.github-corner {
  position: absolute;
  top: 0; right: 0;
  transform: translate3d(0,0,0);
  transition: 333ms transform cubic-bezier(.7,.3,.25,1.15) 1200ms;
  will-change: transform;
  svg {
    width: 4rem; height: 4rem;
  }
  path {
    will-chrange: fill;
  }
  .octo-arm,
  .octo-body {
    fill: $c-black;
    fill: var(--background, #{$c-black});
  }
  .triange {
    fill: $c-white;
    fill: var(--foreground, #{$c-white});
  }
}
.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}

button,
select {
  position: absolute;
  font-family: $t-code;
  box-sizing: border-box;
  
  appearance: none;
  -webkit-appearance: none;
  border: none;
  
  color: $c-white;
  color: var(--foreground, #{$c-white});
  background-color: transparent;
  
  cursor: pointer;
}
select {
  position: relative;
  //max-width: 10rem;
}
.options {
  position: absolute;
  bottom: 0;
  right: 0;
  padding: 1rem;
}

button {
  bottom: 1rem; left: 1rem;
  padding: 0;
  outline: none;
  padding: 0 0 0.5em 0.3em;
  span {
    display: none;
  }
  stonrg {
    display: block;
  }
  svg {
    display: block;
    width: 2rem; height: 2rem;
  }
  path {
    fill: currentColor;
  }
  .line {
    display: none;
  }
}

.isDark {
  button {
    .line {
      display: block;
    }
  }
}
select {
  font-size: 1.5rem;
  //text-decoration: underline;
  appearance: none;
  -webkit-appearance: none;
  border-radius: 0;
  color: turquoise;
  //text-decoration-color: pink;
  padding: 0.25em 1.2em 0.25em 0.3em;
  background-color: rgba(#202124,.5);
  background-size: auto 25%;
  background-repeat: no-repeat;
  background-position: calc(100% - .7rem) 55%;
  background-image: url('data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22utf-8%22%3F%3E%20%3C%21--%20Generator%3A%20IcoMoon.io%20--%3E%20%3C%21DOCTYPE%20svg%20PUBLIC%20%22-//W3C//DTD%20SVG%201.1//EN%22%20%22http%3A//www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd%22%3E%20%3Csvg%20width%3D%22512%22%20height%3D%22512%22%20viewBox%3D%220%200%20512%20512%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20xmlns%3Axlink%3D%22http%3A//www.w3.org/1999/xlink%22%20fill%3D%22%23ffffff%22%3E%3Cpath%20d%3D%22M%2096.00%2C96.00l-96.00%2C96.00l%20256.00%2C256.00l%20256.00-256.00l-96.00-96.00L%20256.00%2C256.00L%2096.00%2C96.00z%22%20%3E%3C/path%3E%3C/svg%3E');
transition: 150ms background-color;
  
  &:focus {
    outline: none;
  }
  &:hover {
    background-color: rgba(#202124,.8);
  }
}
label {
  display: inline-block;
}


@media (max-width: 480px) {
  button {
    left: auto;
    right: 1rem;
    bottom: 7rem;
  }
  select,
  label {
    width: 100%;
  }
}

.label {
  font-size: .7em;
  display: block;
  padding: 0 0.5em;
}
.currentColor {
  pointer-events: none;
  position: absolute;
  left: 1.5rem;
  top: 1rem;
  text-shadow: 0 1px 10px #202124;
  h2 {
    margin: 0;
  }
  span {
    display: block;
    margin-top: .1em;
    &::before {
      position: relative;
      top: -.2em;
      display: inline-block;
      margin-right: 0.25em;
      color: var(--color);
      content: '⬤';
      font-size: .9em;
    }
  }
}
              
            
!

JS

              
                console.clear();

import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r110/build/three.module.js';
import { OrbitControls } from 'https://threejsfundamentals.org/threejs/resources/threejs/r110/examples/jsm/controls/OrbitControls.js';

let cam, scene, root, renderer, controls, layers,
  objects = [], cubeSize = 100, dotSize = 1.25,
  width = window.innerWidth + 1,
  height = window.innerHeight + 1,
  $select = document.querySelector('[data-model]'),
  $selectList = document.querySelector('[data-list]'),
  cDark = '#202124', cLight = '#ffffff',
  bg = cDark, colorMode = 'rgb', spaceCube, isDark = true;

const colorModes = {
  hsv: {
    func: 'hsv',
    x: [0, 360],
    y: [1, 1],
    z: [2, 1]
  },
  hsi: {
    func: 'hsi',
    x: [0, 360],
    y: [1, 1],
    z: [2, 1]
  },
  hsl: {
    func: 'hsl',
    x: [0, 360],
    y: [1, 1],
    z: [2, 1]
  },
  rgb: {
    func: 'rgb',
    x: [0, 255],
    y: [1, 255],
    z: [2, 255]
  },
  xyz: {
    func: 'xyz',
    x: [0, 95.047],
    y: [1, 100],
    z: [2, 108.883]
  },
  'cat02': {
    func: 'cat02',
    x: [0, 95.047],
    y: [1, 104],
    z: [2, 108.883],
  },
  jab: {
    func: 'jzazbz',
    x: [0, .2],
    y: [1, .16, -.16],
    z: [2, .16, -.16],
  },
  luv: {
    func: 'luv',
    x: [0, 100],
    y: [1, 224, -134],
    z: [2, 122, -140],
  },
  lab: {
    func: 'lab',
    z: [0, 100],
    y: [1, 127, -128],
    x: [2, 127, -128]
  },
  oklab: {
    func: 'oklab',
    z: [0, 1],
    y: [1, .3, -.3],
    x: [2, .35, -.35]
  },
  lch: {
    func: 'lch',
    z: [0, 100],
    y: [1, 100],
    x: [2, 0, 360]
  },
  yuv: {
    func: 'yuv',
    z: [0, 255],
    y: [1, 255],
    x: [2, 255]
  },
  hwb: {
    func: 'hwb',
    x: [0, 360],
    y: [1, 1],
    z: [2, 1]
  },
  hcg: {
    func: 'hcg',
    x: [0, 360],
    y: [1, 1],
    z: [2, 1]
  }
};

init();

function onWindowResize() {
  width = window.innerWidth + 1;
  height = window.innerHeight + 1;
  cam.aspect = width / height;
  cam.updateProjectionMatrix();
  renderer.setSize(width, height);
}

let colorList = [];

function fetchList (listname='default') {
  fetch(`https://api.color.pizza/v1/?list=${listname}`).then(d => d.json()).then(d => {
    colorList = d.colors;
    addParticles(colorList, colorMode);
  })
}

fetchList()

let part;

function createCanvasMaterial(color, size = 256) {
  var matCanvas = document.createElement('canvas');
  matCanvas.width = matCanvas.height = size;
  var matContext = matCanvas.getContext('2d');
  // create exture object from canvas.
  var texture = new THREE.Texture(matCanvas);
  // Draw a circle
  var center = size / 2;

  matContext.beginPath();
  matContext.arc(center, center, size / 2, 0, 2 * Math.PI, false);
  matContext.closePath();
  matContext.fillStyle = color;
  matContext.fill();
  // need to set needsUpdate
  texture.needsUpdate = true;
  // return a texture made from the canvas
  return texture;
}

let pMaterial, particles;

function addParticles(colorNames, cMode) {
  // create the particle variables
  const particleCount = colorNames.length;
  
  if (particles) {
    particles.dispose();
  }
  
  particles = new THREE.Geometry();
  
  if (pMaterial) {
    pMaterial.dispose();
  }
  
  dotSize = 255/Math.sqrt(colorNames.length/3) * .4;
  dotSize = Math.max(Math.min(dotSize, 4), 1.25);
  
  pMaterial = new THREE.PointsMaterial({
    vertexColors: THREE.VertexColors,
    size: dotSize,
    alphaMap: createCanvasMaterial('#ffffff', dotSize * 100),
    flatShading: true,
    //fog: false,
    //depthWrite: false,
    transparent: true,
    alphaTest: .5,
    //sizeAttenuation: true,
  });


  let colors = [];

  const mode = colorModes[cMode];

  colorNames.forEach((col, i) => {
    let colorComp;
    let color = new Color({
        "color": col.hex,
        "type": "hex"
    });
    
    if (mode.func === 'oklab') {
      colorComp = linear_srgb_to_oklab(chroma(col.hex).rgb());
    } else if (mode.func === 'yuv') {
      colorComp = yuv(chroma(col.hex).rgb());
    } else if (mode.func === "luv") {
      colorComp = color.luv;
    } else if (mode.func === "xyz" || mode.func === "cat02" || mode.func === "jzazbz") {
      
      colorComp = color.xyz;
      
      if (mode.func === "jzazbz") {
        colorComp = Jzazbz(colorComp);
      }
      
      if (mode.func === "cat02") {
        colorComp = xyz2cat02(colorComp);  
      }
      
    } else if (mode.func === 'hwb') {
      const [hsvH, hsvS, hsvV] = chroma(col.hex).hsv();
      colorComp = [
        hsvH,
        (1 - hsvS) * hsvV,
        1 - hsvV
      ]
    } else {
      colorComp = chroma(col.hex)[mode.func]();
      if (mode.func === 'hcg') {
        colorComp = [colorComp[0],colorComp[1] / 100,colorComp[2] / 100];
      }
    }
    


    let pX = translate(colorComp[mode.x[0]], mode.x[2] || 0, mode.x[1], -cubeSize * .5, cubeSize * .5),
        pY = translate(colorComp[mode.y[0]], mode.y[2] || 0, mode.y[1], -cubeSize * .5, cubeSize * .5),
        pZ = translate(colorComp[mode.z[0]], mode.z[2] || 0, mode.z[1], -cubeSize * .5, cubeSize * .5);

    if (mode.func === 'hsl' || mode.func === 'hsv' || mode.func === 'hsi' || mode.func === 'hcg') {
      let theta = Math.PI * colorComp[mode.x[0]] / 180;
      let r = colorComp[mode.y[0]] * cubeSize;

      if (mode.func === 'hsi') {
        r *= colorComp[mode.z[0]] * 0.75;
      } else if (mode.func === 'hsv') {
        r *= colorComp[mode.z[0]] * 0.5;
      } else if (mode.func === 'hcg') {
        r *= .5;
      } else {
        r *= colorComp[mode.z[0]] < 0.5 ? colorComp[mode.z[0]] : 1 - colorComp[mode.z[0]];
      }

      pY = r * Math.cos(theta);
      pX = r * Math.sin(theta);
    }
    

    if (mode.func === 'lch') {
      let theta = Math.PI * colorComp[mode.x[0]] / 180;
      let r = translate(colorComp[mode.y[0]], 0, mode.y[1], 0, cubeSize * .5);

      pY = r * Math.cos(theta);
      pX = r * Math.sin(theta);
    }


    let particle = new THREE.Vector3(pX, pY, pZ),
          Tcolor = new THREE.Color(col.hex);

    colors.push(Tcolor)

    // add it to the geometry
    particles.vertices.push(particle);
  });


  // create the particle system
  const particleSystem = new THREE.Points(
    particles,
    pMaterial
  );

  particleSystem.name = 'colors';
  particles.colors = colors;

  // add it to the scene
  objects.push(particleSystem)
  scene.add(particleSystem);
  part = particleSystem;
}

renderer.render(scene, cam);


animate();

function setSceneColor(color) {
  scene.background = new THREE.Color(color);
  scene.fog = new THREE.Fog(color, 150, 300); //new THREE.FogExp2(0x000000, 0.0007);
}
/*
var aspect = window.innerWidth / window.innerHeight;
var d = 20;
camera = new THREE.OrthographicCamera( - d * aspect, d * aspect, d, - d, 1, 1000 );

camera.position.set( 20, 20, 20 ); // all components equal
camera.lookAt( scene.position ); // or the origin
*/

function init() {
  cam = new THREE.PerspectiveCamera(75, width / height, 1, 500);
  cam.position.z = cubeSize * 1.5;
  scene = new THREE.Scene();
  setSceneColor(bg)
  root = new THREE.Object3D();

  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(width, height);

  addCube();

  controls = new OrbitControls(cam, renderer.domElement);
  // controls.addEventListener( 'change', render ); // remove when using animation loop
  // enable animation loop when using damping or autorotation
  controls.enableDamping = true;
  controls.dampingFactor = .75;
  controls.enableZoom = true;
  controls.zoomSpeed = .25;
  controls.autoRotate = true;
  controls.autoRotateSpeed = 2.0;
  controls.maxDistance = cubeSize * 1.75;
  controls.maxPolarAngle = Math.PI * 4;
  //controls.minPolarAngle = 0;
  controls.maxAzimuthAngle = Infinity;
  controls.minAzimuthAngle = -Infinity;

  controls.noPan = true;
  controls.noKeys = true;
  //controls.noZoom = true;

  const container = document.querySelector('#container');
  container.appendChild(renderer.domElement);

  window.addEventListener('resize', onWindowResize, false);
  
  document.querySelector('button').addEventListener('click', toggleDarkMode, false);
}

function toggleDarkMode() {
  isDark = !isDark;

  document.querySelector('body').classList.toggle('isDark');
  var newColor = isDark ? cDark : cLight;

  setSceneColor(newColor);
  var colorspace = scene.getObjectByName('colorspace');
  scene.remove(colorspace);
  addCube(isDark ? '#ffffff' : cDark);

  document.documentElement.style.setProperty('--background', newColor);
  document.documentElement.style.setProperty('--foreground', isDark ? '#ffffff' : cDark);
}

function addCube(color) {
  let geometryCube = cube(cubeSize);
  //geometryCube.computeLineDistances();
  const material = new THREE.LineBasicMaterial({
    color: color || 0xffffff,
    linewidth: 1,
    linecap: 'round', //ignored by WebGLRenderer
    linejoin: 'round' //ignored by WebGLRenderer
  });
  
  const colorspace = new THREE.LineSegments(
    geometryCube,
    material
  );

  colorspace.name = 'colorspace';

  objects.push(colorspace);
  scene.add(colorspace);

  spaceCube = colorspace;
}

function cube(size) {
  const h = size * 0.5;
  const geometry = new THREE.Geometry();

  geometry.vertices.push(
    new THREE.Vector3(-h, -h, -h),
    new THREE.Vector3(-h, h, -h),
    new THREE.Vector3(-h, h, -h),
    new THREE.Vector3(h, h, -h),
    new THREE.Vector3(h, h, -h),
    new THREE.Vector3(h, -h, -h),
    new THREE.Vector3(h, -h, -h),
    new THREE.Vector3(-h, -h, -h),
    new THREE.Vector3(-h, -h, h),
    new THREE.Vector3(-h, h, h),
    new THREE.Vector3(-h, h, h),
    new THREE.Vector3(h, h, h),
    new THREE.Vector3(h, h, h),
    new THREE.Vector3(h, -h, h),
    new THREE.Vector3(h, -h, h),
    new THREE.Vector3(-h, -h, h),
    new THREE.Vector3(-h, -h, -h),
    new THREE.Vector3(-h, -h, h),
    new THREE.Vector3(-h, h, -h),
    new THREE.Vector3(-h, h, h),
    new THREE.Vector3(h, h, -h),
    new THREE.Vector3(h, h, h),
    new THREE.Vector3(h, -h, -h),
    new THREE.Vector3(h, -h, h)
  );

  return geometry;
}


function render() {
  const time = Date.now() * 0.0001;
  renderer.render(scene, cam);
  //controls.update();
  objects.forEach(object => {
    //object.rotation.x = 0.25 * time * ( i%2 == 1 ? 1 : -1);
    object.rotation.x = 0.25 * time;
    object.rotation.y = 0.25 * time;
  })
}

function animate() {
  requestAnimationFrame(animate);
  render();
}


$select.addEventListener('change', e => {
  colorMode = $select.value;
  objects = [];
  const colorspace = scene.getObjectByName('colorspace');
  scene.remove(colorspace);
  const colors = scene.getObjectByName('colors');
  scene.remove(colors);
  addParticles(colorList, colorMode);
  addCube(isDark ? cLight : cDark);
}, false);

$selectList.addEventListener('change', e => {
  const listName = $selectList.value;
  objects = [];
  const colorspace = scene.getObjectByName('colorspace');
  scene.remove(colorspace);
  const colors = scene.getObjectByName('colors');
  scene.remove(colors);
  fetchList(listName);
  //addParticles(colorList, colorMode);
  addCube(isDark ? cLight : cDark);
}, false);

function translate(value, low1, high1, low2, high2) {
  return low2 + (high2 - low2) * ((value - low1) / (high1 - low1));
}

const PQ = function perceptual_quantizer(X) {
  const XX = Math.pow(X*1e-4, 0.1593017578125);
  return Math.pow(
    (0.8359375 + 18.8515625*XX) / (1 + 18.6875*XX),
    134.034375); 
};

function Jzazbz([X, Y, Z]) {
  const
    Lp = PQ(0.674207838*X + 0.382799340*Y - 0.047570458*Z),
    Mp = PQ(0.149284160*X + 0.739628340*Y + 0.083327300*Z),
    Sp = PQ(0.070941080*X + 0.174768000*Y + 0.670970020*Z),
    Iz = 0.5 * (Lp + Mp),
    az = 3.524000*Lp - 4.066708*Mp + 0.542708*Sp,
    bz = 0.199076*Lp + 1.096799*Mp - 1.295875*Sp,
    Jz = (0.44 * Iz) / (1 - 0.56*Iz) - 1.6295499532821566e-11;
  return [Jz, az, bz];
}

function cat022hpe(l,m,s) {
  var lh = ( 0.7409792 * l) + (0.2180250 * m) + (0.0410058 * s),
      mh = ( 0.2853532 * l) + (0.6242014 * m) + (0.0904454 * s),
      sh = (-0.0096280 * l) - (0.0056980 * m) + (1.0153260 * s);

  return {lh: lh, mh: mh, sh: sh};
}

function xyz2cat02([x,y,z]) {
  
  const l = ( 0.7328 * x) + (0.4296 * y) - (0.1624 * z),
        m = (-0.7036 * x) + (1.6975 * y) + (0.0061 * z),
        s = ( 0.0030 * x) + (0.0136 * y) + (0.9834 * z);
  
  return [l, m, s];
}

function yuv(rgb) {
  return [
    /*Y*/ rgb[0] * .299000 + rgb[1] * .587000 + rgb[2] * .114000,
    /*U*/ rgb[0] * -.168736 + rgb[1] * -.331264 + rgb[2] * .500000 + 128,
    /*V*/ rgb[0] * .500000 + rgb[1] * -.418688 + rgb[2] * -.081312 + 128
  ]
}

function f(x)
{
    if (x >= 0.0031308)
        return (1.055) * x^(1.0/2.4) - 0.055
    else
        return 12.92 * x;
}

function linear_srgb_to_oklab(rgb) 
{
  rgb = rgb.map(comp => comp / 255);
  
  let l = 0.4121656120 * rgb[0] + 0.5362752080 * rgb[1] + 0.0514575653 * rgb[2];
  let m = 0.2118591070 * rgb[0] + 0.6807189584 * rgb[1] + 0.1074065790 * rgb[2];
  let s = 0.0883097947 * rgb[0] + 0.2818474174 * rgb[1] + 0.6302613616 * rgb[2];

  l = Math.cbrt(l);
  m = Math.cbrt(m);
  s = Math.cbrt(s);

  return [
    (0.2104542553*l + 0.7936177850*m - 0.0040720468*s),
    (1.9779984951*l - 2.4285922050*m + 0.4505937099*s),
    (0.0259040371*l + 0.7827717662*m - 0.8086757660*s),
  ];
}

let rayCaster = new THREE.Raycaster();

document.querySelector('body').addEventListener('mousemove', onDocumentMouseMove);

const $currentColor = document.querySelector('.currentColor');

let currentColorTimer;

function showColor(name, hex) {
  clearTimeout(currentColorTimer);
  $currentColor.innerHTML = `<div><h2>${name}</h2><span>${hex}</span></div>`
  $currentColor.style = `--color: ${hex}`;
  currentColorTimer = setTimeout(() => {
    $currentColor.innerHTML = '';
  }, 3000);
}

// Highlight a color name using a raycaster at some point
function onDocumentMouseMove(event) {
  let mousePosition = {};
  event.preventDefault();
  mousePosition.x = (event.clientX / renderer.domElement.clientWidth) * 2 - 1;
  mousePosition.y = - (event.clientY / renderer.domElement.clientHeight) * 2 + 1;
  rayCaster.setFromCamera(mousePosition, cam);
  var intersects = rayCaster.intersectObjects([scene.getObjectByName('colors')], false);
  if (intersects.length > 0) {
    var descriptions = [];
    for (var i = 0; i < intersects.length; i++) {
      //Only display those points we can SEE due to the near clipping plane.
      //Without this check, the ray caster will list them all, even if they're clipped by the near plane.
      //".distance" is relative to the camera, not absolute world units.
      if (intersects[i].distance > cam.near) {
        var description = "  " + colorList[intersects[i].index].name + ", ";
        description += "  " + colorList[intersects[i].index].hex + ", ";
        description += "  Distance: " + intersects[i].distance.toFixed(2) + ", ";
        description += "  Ray to point dist: " + intersects[i].distanceToRay.toFixed(2) + ", ";
        description += "  Index: " + intersects[i].index + ", ";
        description += "  Coords: [" + intersects[i].point.x.toFixed(2) + ", " + intersects[i].point.y.toFixed(2) + ", " + intersects[i].point.z.toFixed(2) + "]";
        descriptions.push(description);

        showColor(colorList[intersects[i].index].name, colorList[intersects[i].index].hex);
        break;
      }

      if (descriptions.length > 0) {
        console.log("Mouse pointer intersected the following points, closest to furthest: ");
        for (var v = 0; v < descriptions.length; v++)
          console.log(descriptions[v]);
      }
    }
  }
}


              
            
!
999px

Console