{"_id":"@bojurgess/jiang","_rev":"4-b818953a908afb4d01dfe8d36a5d5009","name":"@bojurgess/jiang","dist-tags":{"latest":"1.1.1"},"versions":{"0.1.0":{"name":"@bojurgess/jiang","version":"0.1.0","_id":"@bojurgess/jiang@0.1.0","maintainers":[{"name":"bojurgess","email":"bojurgess@gmail.com"}],"homepage":"https://github.com/bojurgess/jiang#readme","bugs":{"url":"https://github.com/bojurgess/jiang/issues"},"dist":{"shasum":"51d82775a262cfe2335c1821ed64121e82e9fa78","tarball":"https://registry.npmjs.org/@bojurgess/jiang/-/jiang-0.1.0.tgz","fileCount":14,"integrity":"sha512-nEc5dym+aLLm7+bGz5eDIAwS4TmJDfOn7PUBQQxaWB9WuufWKzAyf1zT6ngim9iwA9WOa33rYe5AMwW6hdI2dQ==","signatures":[{"sig":"MEUCIQDjxciOv0BbzFcUajtUaP2Lkx8LNM1abTw92rEuaAvdQwIgcrFYOBf72CIkUzqG7H81HoB0i9F1mgvA3blPv8cXTn4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":3356134},"main":"./bundler/jiang.js","types":"./bundler/jiang.d.ts","exports":{".":{"types":"./bundler/jiang.d.ts","import":"./bundler/jiang.js"},"./web":{"types":"./web/jiang.d.ts","import":"./web/jiang.js"},"./node":{"types":"./node/jiang.d.ts","require":"./node/jiang.js"},"./bundler":{"types":"./bundler/jiang.d.ts","import":"./bundler/jiang.js"}},"gitHead":"df4663d8f7935a4882b38806eb2de2cbcd97d255","_npmUser":{"name":"bojurgess","email":"bojurgess@gmail.com"},"repository":{"url":"git+https://github.com/bojurgess/jiang.git","type":"git"},"_npmVersion":"11.16.0","directories":{},"_nodeVersion":"26.3.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/jiang_0.1.0_1780877329809_0.41178831128339555","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@bojurgess/jiang","version":"1.0.0","_id":"@bojurgess/jiang@1.0.0","maintainers":[{"name":"bojurgess","email":"bojurgess@gmail.com"}],"homepage":"https://github.com/bojurgess/jiang#readme","bugs":{"url":"https://github.com/bojurgess/jiang/issues"},"dist":{"shasum":"3133c5b2202cd5b32868ec54be450f6e259c8ed8","tarball":"https://registry.npmjs.org/@bojurgess/jiang/-/jiang-1.0.0.tgz","fileCount":16,"integrity":"sha512-kNMoDkNniF+bwaL3+6ag0uIDlqKcH3Uaxjl2LyMi8gocItM1tzjPbXqqEIPm3M9oUgew5VhUqy83bVFBlpr+bw==","signatures":[{"sig":"MEUCIEmM/Oh4CcIWrQBoQOt/FqonvoadAEidfaWwdEFM3f/3AiEA5jw9ILvCXuAE8BofYGSD3rHIlL6NBMgbo2p2QHLExr4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bojurgess%2fjiang@1.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":960765},"main":"./bundler/jiang.js","types":"./bundler/jiang.d.ts","exports":{".":{"types":"./bundler/jiang.d.ts","import":"./bundler/jiang.js"},"./web":{"types":"./web/jiang.d.ts","import":"./web/jiang.js"},"./node":{"types":"./node/jiang.d.ts","require":"./node/jiang.js"},"./bundler":{"types":"./bundler/jiang.d.ts","import":"./bundler/jiang.js"}},"gitHead":"69b7e871db113ed2e40af8545745bd047ec2835d","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:181e4036-9647-4d06-8882-0ca6f047455a"}},"repository":{"url":"git+https://github.com/bojurgess/jiang.git","type":"git"},"_npmVersion":"11.16.0","description":"[![npm](https://img.shields.io/npm/v/@bojurgess/jiang)](https://www.npmjs.com/package/@bojurgess/jiang) [![npm downloads](https://img.shields.io/npm/dm/@bojurgess/jiang)](https://www.npmjs.com/package/@bojurgess/jiang) [![bundle size](https://img.shields.","directories":{},"_nodeVersion":"20.20.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/jiang_1.0.0_1780957433445_0.8283270086950467","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@bojurgess/jiang","version":"1.1.0","_id":"@bojurgess/jiang@1.1.0","maintainers":[{"name":"bojurgess","email":"bojurgess@gmail.com"}],"homepage":"https://github.com/bojurgess/jiang#readme","bugs":{"url":"https://github.com/bojurgess/jiang/issues"},"dist":{"shasum":"c71ab08c2d4bd24c07e2cd3223bcce58f500475a","tarball":"https://registry.npmjs.org/@bojurgess/jiang/-/jiang-1.1.0.tgz","fileCount":16,"integrity":"sha512-mjzUxWeIunzvM29PlqjuuFtd/rkRVJoWMlu+nn4TDrS9rCSuMYnvA4PbZM1jGtg9Pivc3RH4rA4v1pAbAgzWjg==","signatures":[{"sig":"MEYCIQDX77U8kjQruIBDsOt9/USSFySz05sn1no7glGjV4aFOwIhAL5I32vRM3Eq6anvRjB4xifAU9B9fbGy2UgBGVLNQUcU","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bojurgess%2fjiang@1.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":970875},"main":"./bundler/jiang.js","types":"./bundler/jiang.d.ts","exports":{".":{"types":"./bundler/jiang.d.ts","import":"./bundler/jiang.js"},"./web":{"types":"./web/jiang.d.ts","import":"./web/jiang.js"},"./node":{"types":"./node/jiang.d.ts","require":"./node/jiang.js"},"./bundler":{"types":"./bundler/jiang.d.ts","import":"./bundler/jiang.js"}},"gitHead":"9cdd018572008f673248c62d06a50128a75ffcc5","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:181e4036-9647-4d06-8882-0ca6f047455a"}},"repository":{"url":"git+https://github.com/bojurgess/jiang.git","type":"git"},"_npmVersion":"12.0.2","description":"[![npm](https://img.shields.io/npm/v/@bojurgess/jiang)](https://www.npmjs.com/package/@bojurgess/jiang) [![npm downloads](https://img.shields.io/npm/dm/@bojurgess/jiang)](https://www.npmjs.com/package/@bojurgess/jiang) [![bundle size](https://img.shields.","directories":{},"_nodeVersion":"24.18.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/jiang_1.1.0_1785444909222_0.813838511379686","host":"s3://npm-registry-packages-npm-production"}},"1.1.1":{"name":"@bojurgess/jiang","version":"1.1.1","repository":{"type":"git","url":"git+https://github.com/bojurgess/jiang.git"},"main":"./bundler/jiang.js","types":"./bundler/jiang.d.ts","exports":{".":{"import":"./bundler/jiang.js","types":"./bundler/jiang.d.ts"},"./bundler":{"import":"./bundler/jiang.js","types":"./bundler/jiang.d.ts"},"./web":{"import":"./web/jiang.js","types":"./web/jiang.d.ts"},"./node":{"require":"./node/jiang.js","types":"./node/jiang.d.ts"}},"gitHead":"9cdd018572008f673248c62d06a50128a75ffcc5","_id":"@bojurgess/jiang@1.1.1","description":"[![npm](https://img.shields.io/npm/v/@bojurgess/jiang)](https://www.npmjs.com/package/@bojurgess/jiang) [![npm downloads](https://img.shields.io/npm/dm/@bojurgess/jiang)](https://www.npmjs.com/package/@bojurgess/jiang) [![bundle size](https://img.shields.","bugs":{"url":"https://github.com/bojurgess/jiang/issues"},"homepage":"https://github.com/bojurgess/jiang#readme","_nodeVersion":"26.3.0","_npmVersion":"11.16.0","dist":{"integrity":"sha512-dKUF8QHl85mYOBpD289D6O66UNvJfuKfqtjNShRemZKMQe/BoH8gewtEATSuukzBfnPHld3hT+zJtp5T/uH5mA==","shasum":"a6df372662cba8fd62ec5ccf885012bb6aba35ff","tarball":"https://registry.npmjs.org/@bojurgess/jiang/-/jiang-1.1.1.tgz","fileCount":16,"unpackedSize":967413,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIBqSiHD7tHUgLulc3tmi+fjfx8KIT+Ztcu+0M/8g1817AiEAz/9QcUmwXbSwapkORtGZFoSwa565taVIC06AtigjHOI="}]},"_npmUser":{"name":"bojurgess","email":"bojurgess@gmail.com"},"directories":{},"maintainers":[{"name":"bojurgess","email":"bojurgess@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/jiang_1.1.1_1785445264160_0.9121278923893339"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-08T00:08:49.672Z","modified":"2026-07-30T21:01:04.579Z","0.1.0":"2026-06-08T00:08:50.005Z","1.0.0":"2026-06-08T22:23:53.623Z","1.1.0":"2026-07-30T20:55:09.386Z","1.1.1":"2026-07-30T21:01:04.317Z"},"bugs":{"url":"https://github.com/bojurgess/jiang/issues"},"homepage":"https://github.com/bojurgess/jiang#readme","repository":{"type":"git","url":"git+https://github.com/bojurgess/jiang.git"},"description":"[![npm](https://img.shields.io/npm/v/@bojurgess/jiang)](https://www.npmjs.com/package/@bojurgess/jiang) [![npm downloads](https://img.shields.io/npm/dm/@bojurgess/jiang)](https://www.npmjs.com/package/@bojurgess/jiang) [![bundle size](https://img.shields.","maintainers":[{"name":"bojurgess","email":"bojurgess@gmail.com"}],"readme":"# jiang\n[![npm](https://img.shields.io/npm/v/@bojurgess/jiang)](https://www.npmjs.com/package/@bojurgess/jiang)\n[![npm downloads](https://img.shields.io/npm/dm/@bojurgess/jiang)](https://www.npmjs.com/package/@bojurgess/jiang)\n[![bundle size](https://img.shields.io/badge/wasm-106kb%20brotli-blue)](https://github.com/bojurgess/jiang)\n> *\"I'll give you a hint, his name's Yang! He won a national math competition in China! He doesn't even speak English! Yeah, I'm sure of the math.\"*\n<p>\n  <img src=\"https://media1.tenor.com/m/j_VrmPkKLwUAAAAC/quant-quantitative.gif\" alt=\"The Big Short\" />\n</p>\n\nA fast, runtime-agnostic colour quantization and palette extraction library for JavaScript, compiled from Rust to WebAssembly.\n\n---\n\n## Installation\n\n```sh\nnpm install @bojurgess/jiang\n```\n\n## Usage\n\n```ts\nimport { extractPalette } from \"@bojurgess/jiang\";\n\nconst response = await fetch(\"https://example.com/image.jpg\");\nconst buf = await response.arrayBuffer();\n\nconst palette = await extractPalette(new Uint8Array(buf), 64);\n\nconsole.log(palette.dominant?.hex);           // \"#a3c2f1\"\nconsole.log(palette.dominant?.rgb);           // [163, 194, 241]\nconsole.log(palette.dominant?.hsl);           // [213.6, 0.72, 0.79]\nconsole.log(palette.dominant?.population);    // 14203\nconsole.log(palette.dominant?.titleTextColor) // \"#000000\"\n```\n\nIf you already have raw RGBA pixel data (e.g. from a canvas):\n\n```ts\nimport { extractPaletteFromRgba } from \"@bojurgess/jiang\";\n\nconst ctx = canvas.getContext(\"2d\");\nconst { data } = ctx.getImageData(0, 0, canvas.width, canvas.height);\n\nconst palette = extractPaletteFromRgba(data, 64);\n```\n\n## Palette\n\nEach call returns a `Palette` object with up to five named swatches:\n\n| Role | Description |\n|---|---|\n| `dominant` | The most representative colour overall |\n| `accent` | A vivid, saturated colour suitable for highlights |\n| `subtle` | A muted, low-saturation colour |\n| `dark` | The darkest usable colour |\n| `light` | The lightest usable colour |\n\nEach swatch (if present) has:\n\n| Field | Type | Description |\n|---|---|---|\n| `hex` | `string` | e.g. `\"#a3c2f1\"` |\n| `rgb` | `[number, number, number]` | e.g. `[163, 194, 241]` |\n| `hsl` | `[number, number, number]` | Hue (0–360), saturation, lightness (0–1) |\n| `population` | `number` | How many pixels fell into this colour's bucket |\n| `titleTextColor` | `string` | `\"#ffffff\"` or `\"#000000\"` — WCAG AA contrast (4.5:1) |\n| `bodyTextColor` | `string` | `\"#ffffff\"` or `\"#000000\"` — large text contrast (3.0:1) |\n\n## Supported formats\n\n`extractPalette` accepts raw bytes for the following formats:\n\n| Format | Notes |\n|---|---|\n| JPEG | Decoded via `zune-jpeg` |\n| PNG | Decoded via `minipng`, all colour modes and bit depths supported |\n| WebP | Decoded via `image-webp`, both lossy and lossless |\n\nFormat is detected automatically from magic bytes, no file extension or mime type needed.\n\nIf you already have decoded RGBA pixel data, use `extractPaletteFromRgba` instead to skip decoding entirely.\n\n## Runtime targets\n\nThe package ships three builds for different environments:\n\n```ts\nimport { extractPalette } from \"@bojurgess/jiang\";          // bundler (Vite, webpack)\nimport { extractPalette } from \"@bojurgess/jiang/web\";      // browser ESM (no bundler)\nimport { extractPalette } from \"@bojurgess/jiang/node\";     // Node.js / Bun\n```\n\nThe wasm binary is identical across all three, only the JS glue differs in how the module is instantiated. This means the library works anywhere JavaScript runs: browsers, Node, Deno, Bun, Cloudflare Workers, and other edge runtimes. It has no dependency on the DOM, `canvas`, or any browser-specific API.\n\nThis also makes it compatible with environments where canvas access is unavailable or restricted, including privacy-hardened browsers (Tor Browser, Firefox with `resistFingerprinting`) where `getImageData` is blocked as a fingerprinting vector.\n\n## Bundle size\n\nThe library is designed to intentionally be lean, making use of decode-only Rust crates for supported formats. The published wasm binary is ~300kb uncompressed, ~106kb brotli.\n\n## Building from source\n\nRequires [wasm-pack](https://rustwasm.github.io/wasm-pack/) and [wasm-opt](https://github.com/WebAssembly/binaryen) (from Binaryen).\n\nOn Arch Linux:\n```sh\nsudo pacman -S binaryen\n```\n\nOn macOS:\n```sh\nbrew install binaryen\n```\n\nThen:\n```sh\nmake all       # builds bundler, web, and node targets with wasm-opt applied\nmake publish   # builds then publishes to npm\n```\n\n`wasm-opt` is run automatically as part of `make all` with `-Oz` to reduce binary size.\n\n## License\n\nMIT","readmeFilename":"README.md"}