@texel/color 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,13 +2,13 @@
2
2
 
3
3
  ![generated](./test/banner.png)
4
4
 
5
- A minimal and modern color library for JavaScript. Mainly useful for real-time applications, generative art, and graphics on the web.
5
+ A minimal and modern color library for JavaScript. Especially useful for real-time applications, generative art, and graphics on the web.
6
6
 
7
7
  - Features: fast color conversion, color difference, gamut mapping, and serialization
8
- - Optimised for speed: approx 20-125 times faster than [Colorjs.io](https://colorjs.io/) (see [benchmarks](#benchmarks))
9
- - Optimised for low memory and minimal allocations: no arrays or objects are created within conversion and gamut mapping functions
10
- - Optimised for compact bundles: zero dependencies, and unused color spaces can be automatically tree-shaked away for small sizes (e.g. ~3.5kb minified if you only require OKLCH to sRGB conversion)
11
- - Optimised for accuracy: [high precision](#accuracy) color space matrices
8
+ - Optimized for speed: approx 20-125 times faster than [Colorjs.io](https://colorjs.io/) (see [benchmarks](#benchmarks))
9
+ - Optimized for low memory and minimal allocations: no arrays or objects are created within conversion and gamut mapping functions
10
+ - Optimized for compact bundles: zero dependencies, and unused color spaces can be automatically tree-shaked away for small sizes (e.g. ~3.5kb minified if you only require OKLCH to sRGB conversion)
11
+ - Optimized for accuracy: [high precision](#accuracy) color space matrices
12
12
  - Focused on a minimal and modern set of color spaces:
13
13
  - xyz (D65), xyz-d50, oklab, oklch, okhsv, okhsl, srgb, srgb-linear, display-p3, display-p3-linear, rec2020, rec2020-linear, a98-rgb, a98-rgb-linear, prophoto-rgb, prophoto-rgb-linear
14
14
 
@@ -307,7 +307,9 @@ sRGB.toBase(in_sRGB, out_linear_sRGB); // linear to gamma transfer function
307
307
 
308
308
  ### Why another library?
309
309
 
310
- [Colorjs](https://colorjs.io/) is fantastic and perhaps the current leading standard in JavaScript, but it's not very practical for creative coding and real-time web applications, where the requirements are often (1) leaner codebases, (2) highly optimized, and (3) minimal GC thrashing. Colorjs is more focused on matching CSS spec, which means it will very likely continue to grow in complexity, and performance will often be marred (for example, `@texel/color` cusp intersection gamut mapping is ~125 times faster than Colorjs and its defaultt CSS based gamut mapping).
310
+ [Colorjs](https://colorjs.io/) is fantastic and perhaps the current leading standard in JavaScript, but it's not very practical for creative coding and real-time web applications, where the requirements are often (1) leaner codebases, (2) highly optimized, and (3) minimal GC thrashing.
311
+
312
+ Colorjs, and simialrly, [Culori](https://culorijs.org/)), are focused on matching CSS spec, which means it will very likely continue to grow in complexity over time, and performance will often be marred (for example, `@texel/color` cusp intersection gamut mapping is ~125 times faster than Colorjs and ~60 times faster than culori).
311
313
 
312
314
  There are many other options such as [color-space](https://www.npmjs.com/package/color-space) or [color-convert](https://www.npmjs.com/package/color-convert), however, these do not support modern spacse such as OKLab and OKHSL, and/or have dubious levels of accuracy (many libraries, for example, do not distinguish between D50 and D65 whitepoints in XYZ).
313
315
 
@@ -337,6 +339,7 @@ If you think the matrices or accuracy could be improved, please open a PR.
337
339
  There are a few benchmarks inside [test](./test):
338
340
 
339
341
  - [bench-colorjs.js](./test/bench-colorjs.js) - run with `npm run bench` to compare against colorjs
342
+ - [bench-culori.js](./test/bench-colorjs.js) - run with node to compare against [culori](https://culorijs.org/)
340
343
  - [bench-node.js](./test/bench-node.js) - run with `npm run bench:node` to get a node profile
341
344
  - [bench-size.js](./test/bench-size.js) - run with `npm run bench:size` to get a small bundle size with esbuild
342
345
 
@@ -359,6 +362,21 @@ Ours: 82.04 ms
359
362
  Speedup: 23.6x faster
360
363
  ```
361
364
 
365
+ And against culori:
366
+
367
+ ```
368
+ Testing with input type: Random Samling in OKLab L Planes
369
+ Conversion OKLCH to P3 --
370
+ Culori: 43.30 ms
371
+ Ours: 12.83 ms
372
+ Speedup: 3.4x faster
373
+
374
+ Gamut Mapping OKLCH to P3 Gamut --
375
+ Culori: 1588.62 ms
376
+ Ours: 23.05 ms
377
+ Speedup: 68.9x faster
378
+ ```
379
+
362
380
  ### Running Locally
363
381
 
364
382
  Clone, `npm install`, then `npm run` to list the available scripts, or `npm t` to run the tests.
@@ -373,4 +391,4 @@ This library was made possible due to the excellent prior work by many developer
373
391
 
374
392
  ## License
375
393
 
376
- MIT, see [LICENSE.md](http://github.com/mattdesl/@texel/color/blob/master/LICENSE.md) for details.
394
+ MIT, see [LICENSE.md](https://github.com/texel-org/color/blob/main/LICENSE.md) for details.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@texel/color",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "a minimal and modern color library",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -13,6 +13,7 @@
13
13
  "canvas-sketch": "^0.7.7",
14
14
  "canvas-sketch-cli": "^1.11.21",
15
15
  "colorjs.io": "^0.5.2",
16
+ "culori": "^4.0.1",
16
17
  "esbuild": "^0.23.0",
17
18
  "faucet": "^0.0.4",
18
19
  "pako": "^2.1.0",
@@ -0,0 +1,115 @@
1
+ import {
2
+ convert,
3
+ OKLCH,
4
+ sRGB,
5
+ sRGBGamut,
6
+ listColorSpaces,
7
+ DisplayP3Gamut,
8
+ DisplayP3,
9
+ gamutMapOKLCH,
10
+ constrainAngle,
11
+ findCuspOKLCH,
12
+ degToRad,
13
+ MapToCuspL,
14
+ OKHSL,
15
+ OKLab,
16
+ OKHSLToOKLab,
17
+ } from "../src/index.js";
18
+
19
+ import { p3, toGamut, oklch, okhsl, converter } from "culori";
20
+
21
+ const N = 256 * 256;
22
+
23
+ // get N OKLCH in-gamut pixels by sampling uniformly from OKHSL cube
24
+ const oklchPixelsInGamut = Array(N)
25
+ .fill()
26
+ .map((_, i, lst) => {
27
+ const t = i / (lst.length - 1);
28
+ // note if we use the standard OKHSL space, it is bound to sRGB gamut...
29
+ // so instead we use the OKHSLToOKLab function and pass P3 gamut
30
+ const okhsl = [t, t, t * 360];
31
+ const oklab = OKHSLToOKLab(okhsl, DisplayP3Gamut);
32
+ return convert(oklab, OKLab, OKLCH);
33
+ });
34
+
35
+ // get N OKLCH pixels by sampling OKLab, many will be out of srgb gamut
36
+ const oklchPixelsRandom = Array(N)
37
+ .fill()
38
+ .map((_, i, lst) => {
39
+ const t = i / (lst.length - 1);
40
+ return convert([t, t * 2 - 1, t * 2 - 1], OKLab, OKLCH);
41
+ });
42
+
43
+ const toP3Gamut = toGamut("p3", "oklch");
44
+ // same perf as p3() it seems
45
+ // const p3Converter = converter("p3");
46
+
47
+ test(oklchPixelsInGamut, "Random Samling in P3 Gamut");
48
+ test(oklchPixelsRandom, "Random Samling in OKLab L Planes");
49
+
50
+ function test(inputPixelsOKLCH, label) {
51
+ console.log("Testing with input type: %s", label);
52
+ const culoriInputsOKLCH = oklchPixelsRandom.map(([l, c, h]) => {
53
+ return {
54
+ mode: "oklch",
55
+ l,
56
+ c,
57
+ h,
58
+ };
59
+ });
60
+
61
+ let now, elapsedCulori, elapsedOurs;
62
+ let tmp = [0, 0, 0];
63
+
64
+ //// conversion
65
+
66
+ tmp = [0, 0, 0];
67
+ now = performance.now();
68
+ for (let oklch of inputPixelsOKLCH) {
69
+ convert(oklch, OKLCH, DisplayP3, tmp);
70
+ }
71
+ elapsedOurs = performance.now() - now;
72
+
73
+ now = performance.now();
74
+ for (let oklchColor of culoriInputsOKLCH) {
75
+ p3(oklchColor);
76
+
77
+ // same perf
78
+ // p3Converter(oklchColor);
79
+ }
80
+ elapsedCulori = performance.now() - now;
81
+ print("Conversion OKLCH to P3");
82
+
83
+ //// gamut
84
+
85
+ tmp = [0, 0, 0];
86
+ now = performance.now();
87
+ for (let oklch of inputPixelsOKLCH) {
88
+ gamutMapOKLCH(oklch, DisplayP3Gamut, DisplayP3, tmp);
89
+ }
90
+ elapsedOurs = performance.now() - now;
91
+
92
+ now = performance.now();
93
+ for (let oklchColor of culoriInputsOKLCH) {
94
+ toP3Gamut(oklchColor);
95
+ }
96
+ elapsedCulori = performance.now() - now;
97
+ print("Gamut Mapping OKLCH to P3 Gamut");
98
+
99
+ function print(label) {
100
+ console.log("%s --", label);
101
+ console.log("Culori: %s ms", elapsedCulori.toFixed(2));
102
+ console.log("Ours: %s ms", elapsedOurs.toFixed(2));
103
+ if (elapsedCulori > elapsedOurs)
104
+ console.log(
105
+ "Speedup: %sx faster",
106
+ (elapsedCulori / elapsedOurs).toFixed(1)
107
+ );
108
+ else
109
+ console.log(
110
+ "Slowdown: %sx slower",
111
+ (elapsedOurs / elapsedCulori).toFixed(1)
112
+ );
113
+ console.log();
114
+ }
115
+ }
@@ -1,3 +1,12 @@
1
- import * as colors from "../";
1
+ // To test @texel/color (~3.5 kb)
2
+ import * as colors from "../src/index.js";
2
3
  const rgb = colors.convert([0.5, 0.15, 30], colors.OKLCH, colors.sRGB);
3
4
  console.log(rgb);
5
+
6
+ // To test colorjs.io (~55.3 kb)
7
+ // import Color from "colorjs.io";
8
+ // console.log(new Color("oklch", [0.5, 0.15, 30]).to("srgb").coords);
9
+
10
+ // To test Culori (~43.2 kb)
11
+ // import { rgb } from "culori";
12
+ // console.log(rgb({ mode: "oklch", l: 0.5, c: 0.15, h: 30 }));