palette-shader 0.7.0 → 0.8.0

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
@@ -24,7 +24,7 @@ So if one of your palette colors only claims a tiny sliver, it lives very close
24
24
  npm install palette-shader
25
25
  ```
26
26
 
27
- No dependencies — only a browser with WebGL support is required.
27
+ No runtime dependencies — only a browser with WebGL2 support is required. Colors must be passed as `[r, g, b]` arrays with values in the `0–1` range (linear sRGB). Use a library such as [culori](https://culorijs.org/) to convert from CSS strings if needed.
28
28
 
29
29
  ---
30
30
 
@@ -32,17 +32,21 @@ No dependencies — only a browser with WebGL support is required.
32
32
 
33
33
  ```js
34
34
  import { PaletteViz } from 'palette-shader';
35
+ import { converter } from 'culori';
36
+
37
+ const toSRGB = converter('srgb');
38
+ const toRGB = (hex) => { const c = toSRGB(hex); return [c.r, c.g, c.b]; };
35
39
 
36
40
  // option A — pass a container, canvas is appended automatically
37
41
  const viz = new PaletteViz({
38
- palette: ['#264653', '#2a9d8f', '#e9c46a', '#f4a261', '#e76f51'],
42
+ palette: ['#264653', '#2a9d8f', '#e9c46a', '#f4a261', '#e76f51'].map(toRGB),
39
43
  container: document.querySelector('#app'),
40
44
  width: 512,
41
45
  height: 512,
42
46
  });
43
47
 
44
48
  // option B — no container, place the canvas yourself
45
- const viz = new PaletteViz({ palette: ['#264653', '#2a9d8f', '#e9c46a'] });
49
+ const viz = new PaletteViz({ palette: ['#264653', '#2a9d8f', '#e9c46a'].map(toRGB) });
46
50
  document.querySelector('#app').appendChild(viz.canvas);
47
51
  ```
48
52
 
@@ -58,7 +62,7 @@ All options are optional. The palette defaults to a random 20-colour set.
58
62
 
59
63
  | Option | Type | Default | Description |
60
64
  | ---------------- | ------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------ |
61
- | `palette` | `string[]` | random | CSS colour strings (`#hex`, `rgb()`, `hsl()`, …) |
65
+ | `palette` | `[number, number, number][]` | random | sRGB colours as `[r, g, b]` arrays, each component in the `0–1` range |
62
66
  | `container` | `HTMLElement` | `undefined` | Element the canvas is appended to. Omit and use `viz.canvas` to place it yourself |
63
67
  | `width` | `number` | `512` | Canvas width in CSS pixels |
64
68
  | `height` | `number` | `512` | Canvas height in CSS pixels |
@@ -78,7 +82,7 @@ All options are optional. The palette defaults to a random 20-colour set.
78
82
  Every constructor option is also a live setter/getter. Assigning any of them re-renders immediately via `requestAnimationFrame`.
79
83
 
80
84
  ```js
81
- viz.palette = ['#ff0000', '#00ff00', '#0000ff'];
85
+ viz.palette = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
82
86
  viz.position = 0.5;
83
87
  viz.colorModel = 'okhslPolar';
84
88
  viz.distanceMetric = 'deltaE2000';
@@ -112,7 +116,7 @@ window.addEventListener('resize', () => viz.resize(window.innerWidth * 0.5));
112
116
  Update a single palette entry without rebuilding the whole texture.
113
117
 
114
118
  ```js
115
- viz.setColor('#e63946', 2);
119
+ viz.setColor([0.902, 0.224, 0.275], 2);
116
120
  ```
117
121
 
118
122
  ### `addColor(color, index?)`
@@ -120,17 +124,17 @@ viz.setColor('#e63946', 2);
120
124
  Insert a colour at `index` (appends if omitted).
121
125
 
122
126
  ```js
123
- viz.addColor('#a8dadc'); // append
124
- viz.addColor('#457b9d', 0); // prepend
127
+ viz.addColor([0.659, 0.855, 0.863]); // append
128
+ viz.addColor([0.271, 0.482, 0.616], 0); // prepend
125
129
  ```
126
130
 
127
131
  ### `removeColor(index | color)`
128
132
 
129
- Remove a palette entry by index or by colour string.
133
+ Remove a palette entry by index or by colour value.
130
134
 
131
135
  ```js
132
136
  viz.removeColor(0);
133
- viz.removeColor('#a8dadc');
137
+ viz.removeColor([0.659, 0.855, 0.863]);
134
138
  ```
135
139
 
136
140
  ### `destroy()`
@@ -255,7 +259,7 @@ viz.canvas.style.borderRadius = '50%';
255
259
  ### Multiple synchronised views
256
260
 
257
261
  ```js
258
- const palette = ['#264653', '#2a9d8f', '#e9c46a'];
262
+ const palette = ['#264653', '#2a9d8f', '#e9c46a'].map(toRGB);
259
263
  const shared = { palette, width: 256, height: 256, container: document.querySelector('#views') };
260
264
 
261
265
  const views = [
@@ -277,7 +281,7 @@ document.querySelector('#slider').addEventListener('input', (e) => {
277
281
 
278
282
  ```js
279
283
  const viz = new PaletteViz({
280
- palette,
284
+ palette: ['#264653', '#2a9d8f', '#e9c46a'].map(toRGB),
281
285
  outlineWidth: 2,
282
286
  container: document.querySelector('#app'),
283
287
  });
@@ -300,7 +304,7 @@ import { paletteToRGBA, randomPalette, fragmentShader } from 'palette-shader';
300
304
 
301
305
  // Get raw RGBA bytes (Uint8Array, sRGB, 4 bytes per color)
302
306
  // Useful for building your own WebGL texture or processing palette data
303
- const rgba = paletteToRGBA(['#ff0000', '#00ff00', '#0000ff']);
307
+ const rgba = paletteToRGBA([[1, 0, 0], [0, 1, 0], [0, 0, 1]]);
304
308
 
305
309
  // Quick random palette for prototyping
306
310
  const palette = randomPalette(16);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "palette-shader",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Dependency-free WebGL2 shader that maps any colour palette across perceptual colour spaces — OKHsv, OKHsl, OKLCH and more.",
5
5
  "keywords": [
6
6
  "color",
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import {
2
- ColorString,
2
+ ColorRGB,
3
3
  ColorList,
4
4
  PaletteVizOptions,
5
5
  SupportedColorModels,
@@ -203,33 +203,6 @@ void main() {
203
203
  fragColor = center;
204
204
  }`;
205
205
 
206
- // ── Color parsing ──────────────────────────────────────────────────────────────
207
- // Use a canvas 2D context as a free CSS color parser — handles hex, rgb(),
208
- // hsl(), named colors, etc. Lazy-initialised to avoid issues at module load time.
209
-
210
- let _colorCtx: CanvasRenderingContext2D | null = null;
211
-
212
- function cssToSRGB(color: string): [number, number, number] {
213
- if (!_colorCtx) {
214
- const c = document.createElement('canvas');
215
- c.width = c.height = 1;
216
- _colorCtx = c.getContext('2d')!;
217
- }
218
- _colorCtx.fillStyle = '#000000'; // reset before setting
219
- _colorCtx.fillStyle = color;
220
- const v = _colorCtx.fillStyle; // browser normalises to '#rrggbb' or 'rgba(...)'
221
- if (v[0] === '#') {
222
- return [
223
- parseInt(v.slice(1, 3), 16) / 255,
224
- parseInt(v.slice(3, 5), 16) / 255,
225
- parseInt(v.slice(5, 7), 16) / 255,
226
- ];
227
- }
228
- // rgba(r, g, b, a) fallback
229
- const m = v.match(/[\d.]+/g)!;
230
- return [+m[0] / 255, +m[1] / 255, +m[2] / 255];
231
- }
232
-
233
206
  // ── Palette helpers ────────────────────────────────────────────────────────────
234
207
 
235
208
  // Returns the palette as a flat RGBA Uint8Array (sRGB, 1×N texture row).
@@ -237,15 +210,10 @@ function cssToSRGB(color: string): [number, number, number] {
237
210
  export const paletteToRGBA = (palette: ColorList): Uint8Array => {
238
211
  const data = new Uint8Array(palette.length * 4);
239
212
  palette.forEach((color, i) => {
240
- try {
241
- const [r, g, b] = cssToSRGB(color);
242
- data[i * 4 + 0] = Math.round(r * 255);
243
- data[i * 4 + 1] = Math.round(g * 255);
244
- data[i * 4 + 2] = Math.round(b * 255);
245
- data[i * 4 + 3] = 255;
246
- } catch {
247
- console.error(`Invalid color: ${color}`);
248
- }
213
+ data[i * 4 + 0] = Math.round(color[0] * 255);
214
+ data[i * 4 + 1] = Math.round(color[1] * 255);
215
+ data[i * 4 + 2] = Math.round(color[2] * 255);
216
+ data[i * 4 + 3] = 255;
249
217
  });
250
218
  return data;
251
219
  };
@@ -254,11 +222,7 @@ export const paletteToRGBA = (palette: ColorList): Uint8Array => {
254
222
  export const paletteToTexture = paletteToRGBA;
255
223
 
256
224
  export const randomPalette = (size = 20): ColorList =>
257
- Array.from(
258
- { length: size },
259
- () =>
260
- `rgb(${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)})`,
261
- );
225
+ Array.from({ length: size }, () => [Math.random(), Math.random(), Math.random()] as ColorRGB);
262
226
 
263
227
  // ── WebGL helpers ──────────────────────────────────────────────────────────────
264
228
 
@@ -631,24 +595,29 @@ export class PaletteViz {
631
595
  return this.#palette;
632
596
  }
633
597
 
634
- setColor(color: ColorString, index: number): void {
598
+ setColor(color: ColorRGB, index: number): void {
635
599
  if (index < 0 || index >= this.#palette.length) throw new Error(`Index ${index} out of range`);
636
600
  this.#palette[index] = color;
637
601
  uploadPaletteTexture(this.#gl, this.#texture!, this.#palette);
638
602
  this.#paint();
639
603
  }
640
604
 
641
- addColor(color: ColorString, index?: number): void {
605
+ addColor(color: ColorRGB, index?: number): void {
642
606
  this.#palette.splice(index ?? this.#palette.length, 0, color);
643
607
  uploadPaletteTexture(this.#gl, this.#texture!, this.#palette);
644
608
  this.#paint();
645
609
  }
646
610
 
647
611
  removeColor(index: number): void;
648
- removeColor(color: ColorString): void;
649
- removeColor(indexOrColor: number | ColorString): void {
612
+ removeColor(color: ColorRGB): void;
613
+ removeColor(indexOrColor: number | ColorRGB): void {
650
614
  const index =
651
- typeof indexOrColor === 'number' ? indexOrColor : this.#palette.indexOf(indexOrColor);
615
+ typeof indexOrColor === 'number'
616
+ ? indexOrColor
617
+ : this.#palette.findIndex(
618
+ (c) =>
619
+ c[0] === indexOrColor[0] && c[1] === indexOrColor[1] && c[2] === indexOrColor[2],
620
+ );
652
621
  if (index === -1) throw new Error('Color not found in palette');
653
622
  if (index < 0 || index >= this.#palette.length) throw new Error(`Index ${index} out of range`);
654
623
  this.#palette.splice(index, 1);
package/src/types.ts CHANGED
@@ -1,5 +1,5 @@
1
- export type ColorString = string;
2
- export type ColorList = ColorString[];
1
+ export type ColorRGB = [number, number, number];
2
+ export type ColorList = ColorRGB[];
3
3
 
4
4
  export type SupportedColorModels =
5
5
  | 'rgb'