{"_id":"ditherwave","name":"ditherwave","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"ditherwave","version":"0.1.0","description":"A tiny WebGL2 dithering primitive for React — wrap any image, video, or canvas, or drop in an animated grainy background.","license":"MIT","author":{"name":"sahilsaini5","url":"https://github.com/sahilsaini5"},"repository":{"type":"git","url":"git+https://github.com/sahilsaini5/ditherwave.git"},"homepage":"https://github.com/sahilsaini5/ditherwave#readme","bugs":{"url":"https://github.com/sahilsaini5/ditherwave/issues"},"engines":{"node":">=18"},"keywords":["dither","dithering","bayer","halftone","ascii","background","gradient","noise","react","webgl","webgl2"],"type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","unpkg":"./dist/vanilla.umd.global.js","jsdelivr":"./dist/vanilla.umd.global.js","peerDependenciesMeta":{"react":{"optional":true}},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./vanilla":{"types":"./dist/vanilla.d.ts","import":"./dist/vanilla.js","require":"./dist/vanilla.cjs"}},"sideEffects":false,"scripts":{"build":"tsup","dev":"tsup --watch","size":"gzip -c dist/index.js | wc -c"},"peerDependencies":{"react":">=18"},"devDependencies":{"@types/react":"^18.3.12","react":"^18.3.1","tsup":"^8.3.5","typescript":"^5.6.3"},"gitHead":"51f6782a40c8dc665019a20a6758b387119967f6","_id":"ditherwave@0.1.0","_nodeVersion":"24.14.0","_npmVersion":"11.9.0","dist":{"integrity":"sha512-tKUl2gotYf5pLNMr0JxgY+hJhd96kbwh9KI4jQrXLN425knUv+M2uR/et2K+JYr0jbcgU3TAOyBkhs/FtESdhw==","shasum":"a87bbb896064648fd3564976abbed5be4b2e10f1","tarball":"https://registry.npmjs.org/ditherwave/-/ditherwave-0.1.0.tgz","fileCount":19,"unpackedSize":536912,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIGmIQPcxqHp+voKxNt79eW4zQixATgnMkFwFFCzOJ/WTAiEA0ntFi/DxSyboSLv2tiA6mFbKB+97EVJ7RUl2AdL5vzk="}]},"_npmUser":{"name":"realsahilsaini","email":"thisissahilsaini@gmail.com"},"directories":{},"maintainers":[{"name":"realsahilsaini","email":"thisissahilsaini@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ditherwave_0.1.0_1777185572949_0.06067038328242136"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-26T06:39:32.890Z","0.1.0":"2026-04-26T06:39:33.099Z","modified":"2026-04-26T06:39:33.251Z"},"maintainers":[{"name":"realsahilsaini","email":"thisissahilsaini@gmail.com"}],"description":"A tiny WebGL2 dithering primitive for React — wrap any image, video, or canvas, or drop in an animated grainy background.","homepage":"https://github.com/sahilsaini5/ditherwave#readme","keywords":["dither","dithering","bayer","halftone","ascii","background","gradient","noise","react","webgl","webgl2"],"repository":{"type":"git","url":"git+https://github.com/sahilsaini5/ditherwave.git"},"author":{"name":"sahilsaini5","url":"https://github.com/sahilsaini5"},"bugs":{"url":"https://github.com/sahilsaini5/ditherwave/issues"},"license":"MIT","readme":"<div align=\"center\">\n\n# ditherwave\n\n**A tiny WebGL2 dithering primitive for React.**\nWrap any image, video, or canvas in `<Dither>` — or drop in `<DitheredWaves>` for an animated grainy background. Bayer, halftone, ASCII, or error-diffusion. Live, animated, **under 8kb gzipped**.\n\n[![npm](https://img.shields.io/npm/v/ditherwave?style=flat-square)](https://www.npmjs.com/package/ditherwave)\n[![bundle](https://img.shields.io/bundlephobia/minzip/ditherwave?style=flat-square)](https://bundlephobia.com/package/ditherwave)\n[![license](https://img.shields.io/npm/l/ditherwave?style=flat-square)](./LICENSE)\n\n</div>\n\n---\n\n## install\n\n```sh\nnpm install ditherwave\n# or: pnpm add ditherwave · yarn add ditherwave · bun add ditherwave\n```\n\nReact is an **optional** peer dep — only needed if you import the React components. Zero runtime dependencies otherwise.\n\n### use without React (plain HTML / CSS / JS)\n\n```html\n<canvas id=\"bg\" style=\"width:100%;height:100vh\"></canvas>\n\n<script type=\"module\">\n  import { createDitheredWaves } from 'https://esm.sh/ditherwave/vanilla';\n  createDitheredWaves(document.getElementById('bg'), {\n    waveColor: '#39ff14',\n    baseColor: '#050605',\n  });\n</script>\n```\n\nOr via npm + a `<script>` tag (no bundler):\n\n```html\n<canvas id=\"bg\"></canvas>\n<script src=\"./node_modules/ditherwave/dist/vanilla.umd.global.js\"></script>\n<script>\n  Dither.createDitheredWaves(document.getElementById('bg'), { /* ...opts */ });\n</script>\n```\n\nThe `ditherwave/vanilla` entry contains only the framework-free helpers (`createDither`, `createDitheredWaves`, `dither`) and pulls in zero React code.\n\n## use\n\nTwo primitives. Pick one.\n\n### `<Dither>` — dither existing content\n\n```tsx\nimport { Dither } from 'ditherwave';\n\n<Dither mode=\"bayer\" palette={['#0f380f', '#9bbc0f']} animate>\n  <img src=\"https://picsum.photos/800\" alt=\"\" />\n</Dither>\n```\n\nDrop an `<img>`, `<video>`, or `<canvas>` inside and it'll be dithered into the output canvas every frame. Sources update live — video textures via `requestVideoFrameCallback`, canvases every frame.\n\n### `<DitheredWaves>` — animated noise background\n\n```tsx\nimport { DitheredWaves } from 'ditherwave';\n\n<DitheredWaves\n  waveColor=\"#9bbc0f\"\n  baseColor=\"#0f380f\"\n  pixelSize={3}\n  colorNum={4}\n/>\n```\n\nA full-bleed animated fBm-noise pattern, bayer-dithered, quantised to `colorNum` levels per channel. Drop-in hero background.\n\n## api\n\n### `<Dither>` props\n\n| Prop             | Type                                              | Default        | Notes |\n| ---------------- | ------------------------------------------------- | -------------- | ----- |\n| `mode`           | `'bayer' \\| 'floyd' \\| 'dots' \\| 'ascii'`         | `'bayer'`      | |\n| `resolution`     | `number`                                          | `256`          | output pixel grid on the short edge |\n| `palette`        | `string[]`                                        | `['#0d0c0a','#ece8df']` | 2–8 hex colors |\n| `intensity`      | `number`                                          | `1`            | 0–1, strength of the pattern |\n| `animate`        | `boolean`                                         | `false`        | slow drift on the threshold pattern |\n| `matrixSize`     | `2 \\| 4 \\| 8`                                     | `8`            | bayer only |\n| `charset`        | `string`                                          | `' .:-=+*#%@'` | ascii only, density ramp low→high |\n| `pauseOffscreen` | `boolean`                                         | `true`         | uses IntersectionObserver |\n| `fallback`       | `ReactNode`                                       | `children`     | rendered when WebGL2 isn't available |\n\n### `<DitheredWaves>` props\n\n| Prop                     | Type      | Default     |\n| ------------------------ | --------- | ----------- |\n| `mode`                   | `'bayer' \\| 'floyd' \\| 'dots' \\| 'ascii'` | `'bayer'` |\n| `waveColor`              | hex       | `'#7e7e7e'` |\n| `baseColor`              | hex       | `'#000000'` |\n| `waveSpeed`              | `number`  | `0.05`      |\n| `waveFrequency`          | `number`  | `3`         |\n| `waveAmplitude`          | `number`  | `0.3`       |\n| `pixelSize`              | `number`  | `2`         |\n| `colorNum`               | `number`  | `4`         |\n| `matrixSize`             | `2 \\| 4 \\| 8` | `8`     |\n| `enableMouseInteraction` | `boolean` | `true`      |\n| `mouseRadius`            | `number`  | `1`         |\n| `disableAnimation`       | `boolean` | `false`     |\n\nAlso exported:\n\n- `useDither(canvasRef, sourceRef, options)` — imperative hook.\n- `createDither(target, source, options)` — framework-agnostic; returns `{ destroy, setOptions, render }`.\n- `createDitheredWaves(target, options)` — same, for the noise background.\n- `dither(source, target, options)` — alias for non-React users.\n\n## modes\n\n**bayer** — ordered dithering. Cheap, GPU-friendly, parallelizes perfectly. The Obra Dinn / Gameboy look.\n\n**floyd** — error diffusion. True Floyd-Steinberg is sequential and can't run in a single fragment pass. This mode ships a **Riemersma-style Hilbert-walk approximation** that reads neighbours and mixes a low-discrepancy threshold — it's *not* true FS, and we won't pretend it is. Looks close enough that you'll only notice in A/B.\n\n**dots** — newspaper halftone. Rotated 15° for authentic print feel. Dot radius scales with local darkness. The pretty one.\n\n**ascii** — luminance-indexed glyph atlas built at init. Uses the system monospace stack for ~0 bundle cost; pass your own `charset` for a denser ramp.\n\n## performance\n\n- 60fps on a 2020 MacBook Air at 1080p / resolution 256.\n- Zero per-frame allocations after init.\n- `devicePixelRatio` capped at 2 — dithering at 3× is wasteful.\n- Pauses when scrolled offscreen (IntersectionObserver, on by default).\n- Listens for `webglcontextlost`; reinit on `webglcontextrestored` is your call.\n\n## size\n\nLibrary core ships ESM, CJS, and UMD with sourcemaps. The full public surface tree-shakes down to under 8kb gzipped. Use `pnpm --filter ditherwave size` to check locally.\n\n## credits\n\n`<DitheredWaves>` is heavily inspired by [reactbits' Dither background](https://reactbits.dev/backgrounds/dither) — same domain-warped fBm noise + Bayer post-process, reimplemented in raw WebGL2 to keep the bundle small and avoid the three.js / postprocessing dependency. If you only need the background and you're already using `@react-three/fiber`, use theirs.\n\n## license\n\nMIT.\n","readmeFilename":"README.md","_rev":"1-a1342f597684521c82c530bc8e22cfb7"}