{"_id":"@anam-ai/halftone-shader","_rev":"4-5d143aeb5d5fbe19ddd21a040c43c499","name":"@anam-ai/halftone-shader","dist-tags":{"latest":"0.1.3"},"versions":{"0.1.0":{"name":"@anam-ai/halftone-shader","version":"0.1.0","_id":"@anam-ai/halftone-shader@0.1.0","maintainers":[{"name":"ao-anam","email":"alex@anam.ai"},{"name":"benanam","email":"ben@anam.ai"},{"name":"peter-anamai","email":"peter@anam.ai"},{"name":"robbieanam","email":"robbie@anam.ai"},{"name":"sm-anam","email":"sam@anam.ai"},{"name":"smidge","email":"harry@smaje.net"},{"name":"ema952","email":"ema@anam.ai"}],"dist":{"shasum":"66a5ed7d1d17d15507d23ab14c0ca1d22da2f519","tarball":"https://registry.npmjs.org/@anam-ai/halftone-shader/-/halftone-shader-0.1.0.tgz","fileCount":8,"integrity":"sha512-OmKCrlJt9/yRtgdnTqy0u6LRshqJWMKkThBmmObePXnzKauvYvJiu9XkLX9Qwsw1fMnDPmdvlAbk/NafAu3XqA==","signatures":[{"sig":"MEQCIAwS4RYTTyLb7L7PhBUOhDY2G4+cJ+11t+bNBN9qdtucAiAgTeVfY0lN42SbFVTBncF3eljXztrI0ZV8UMFciyMALw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":65164},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.mjs","gitHead":"029ac153e53ae7bcc638685fc45d95108647ba6d","scripts":{"dev":"vite","build":"vite build","preview":"vite preview"},"_npmUser":{"name":"ema952","email":"ema@anam.ai"},"_npmVersion":"10.8.2","description":"A React component that renders a real-time WebGL halftone effect on images. No pre-exported files — the effect runs live in the browser on a `<canvas>` element, making it the right format for web applications.","directories":{},"_nodeVersion":"20.19.4","_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.0","typescript":"^5.7.0","@types/node":"^25.3.5","@types/react":"^19.0.0","vite-plugin-dts":"^4.5.4","@types/react-dom":"^19.0.0","@vitejs/plugin-react":"^4.3.4"},"peerDependencies":{"react":">=18","react-dom":">=18"},"_npmOperationalInternal":{"tmp":"tmp/halftone-shader_0.1.0_1774874247911_0.6969722799207021","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@anam-ai/halftone-shader","version":"0.1.1","keywords":["halftone","webgl","glsl","shader","canvas","react","image-effect"],"author":{"name":"Ema","email":"ema@anam.ai"},"license":"MIT","_id":"@anam-ai/halftone-shader@0.1.1","maintainers":[{"name":"ao-anam","email":"alex@anam.ai"},{"name":"benanam","email":"ben@anam.ai"},{"name":"peter-anamai","email":"peter@anam.ai"},{"name":"robbieanam","email":"robbie@anam.ai"},{"name":"sm-anam","email":"sam@anam.ai"},{"name":"smidge","email":"harry@smaje.net"},{"name":"ema952","email":"ema@anam.ai"}],"homepage":"https://half-tone-shader.vercel.app","bugs":{"url":"https://github.com/anam-org/anam-halftone-shader/issues"},"dist":{"shasum":"41aa5d3e84e24c3f5d442c01c66a421bd308c36e","tarball":"https://registry.npmjs.org/@anam-ai/halftone-shader/-/halftone-shader-0.1.1.tgz","fileCount":8,"integrity":"sha512-gTFcCfLNc3rBpE4v8NmOkN0OI3mg/woW/d3uzrxq8zmT/y5lXST6i1uIURryjRs4nl41+DapRplr7wWOSuHqDg==","signatures":[{"sig":"MEYCIQDNHHv9N+ECrwntfqILr7vFNwMnOXutpF9WkzwZRxzj/AIhAP3SgGsraUkWUWDT4eAjNMPx7R1CnF7Sz8kr3mD1tqbH","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":39213},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.cjs"}},"gitHead":"1a4b71ab9d8672d3fb5faf1e1ba20f9a8222e601","scripts":{"build":"vite build","prepublishOnly":"npm run build"},"_npmUser":{"name":"ema952","email":"ema@anam.ai"},"repository":{"url":"git+https://github.com/anam-org/anam-halftone-shader.git","type":"git"},"_npmVersion":"10.8.2","description":"Real-time WebGL halftone effect for images. Framework-agnostic engine with a React wrapper.","directories":{},"sideEffects":false,"_nodeVersion":"20.19.4","_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.0","typescript":"^5.7.0","@types/node":"^25.3.5","@types/react":"^19.0.0","vite-plugin-dts":"^4.5.4","@types/react-dom":"^19.0.0","@vitejs/plugin-react":"^4.3.4"},"peerDependencies":{"react":">=18","react-dom":">=18"},"_npmOperationalInternal":{"tmp":"tmp/halftone-shader_0.1.1_1774901785833_0.40100832654679897","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@anam-ai/halftone-shader","version":"0.1.2","keywords":["halftone","webgl","glsl","shader","canvas","react","image-effect"],"author":{"name":"Ema","email":"ema@anam.ai"},"license":"MIT","_id":"@anam-ai/halftone-shader@0.1.2","maintainers":[{"name":"ao-anam","email":"alex@anam.ai"},{"name":"benanam","email":"ben@anam.ai"},{"name":"peter-anamai","email":"peter@anam.ai"},{"name":"robbieanam","email":"robbie@anam.ai"},{"name":"sm-anam","email":"sam@anam.ai"},{"name":"smidge","email":"harry@smaje.net"},{"name":"ema952","email":"ema@anam.ai"}],"homepage":"https://half-tone-shader.vercel.app","bugs":{"url":"https://github.com/anam-org/anam-halftone-shader/issues"},"dist":{"shasum":"056033d1f42f2759e7e775c6137f8d0318800383","tarball":"https://registry.npmjs.org/@anam-ai/halftone-shader/-/halftone-shader-0.1.2.tgz","fileCount":8,"integrity":"sha512-2xJF0PdRuF4CPwLkAvzGdnUBQUfzLn+fY7gkKK8dQh1w6ZNOHT6LBWqF+x1MCQclic+9np5pd+yW3tO8cPGjww==","signatures":[{"sig":"MEUCIHYbjfPLv2Dcb94AB03K2DzEa4RPlPi4Oo0O2OEyH59nAiEAjzzG15J0kYXJzz2TcP2eEHvKlI1bVunHZtGH0xOW5oI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":39229},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.cjs"}},"gitHead":"512dfbcd0c8e71a4911549479bda23ed6837ad55","scripts":{"build":"vite build","prepublishOnly":"npm run build"},"_npmUser":{"name":"ema952","email":"ema@anam.ai"},"repository":{"url":"git+https://github.com/anam-org/anam-halftone-shader.git","type":"git"},"_npmVersion":"10.8.2","description":"Real-time WebGL halftone effect for images. Framework-agnostic engine with a React wrapper.","directories":{},"sideEffects":false,"_nodeVersion":"20.19.4","_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.0","typescript":"^5.7.0","@types/node":"^25.3.5","@types/react":"^19.0.0","vite-plugin-dts":"^4.5.4","@types/react-dom":"^19.0.0","@vitejs/plugin-react":"^4.3.4"},"peerDependencies":{"react":">=18","react-dom":">=18"},"_npmOperationalInternal":{"tmp":"tmp/halftone-shader_0.1.2_1774947536280_0.5486206545904184","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@anam-ai/halftone-shader","version":"0.1.3","description":"Real-time WebGL halftone effect for images. Framework-agnostic engine with a React wrapper.","author":{"name":"Ema","email":"ema@anam.ai"},"license":"MIT","homepage":"https://half-tone-shader.vercel.app","repository":{"type":"git","url":"git+https://github.com/anam-org/anam-halftone-shader.git"},"keywords":["halftone","webgl","glsl","shader","canvas","react","image-effect"],"type":"module","main":"./dist/index.cjs","module":"./dist/index.mjs","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.cjs"}},"sideEffects":false,"scripts":{"build":"vite build","prepublishOnly":"npm run build"},"peerDependencies":{"react":">=18","react-dom":">=18"},"devDependencies":{"@types/node":"^25.3.5","@types/react":"^19.0.0","@types/react-dom":"^19.0.0","@vitejs/plugin-react":"^4.3.4","typescript":"^5.7.0","vite":"^6.0.0","vite-plugin-dts":"^4.5.4"},"_id":"@anam-ai/halftone-shader@0.1.3","gitHead":"a9c88e45667c7cc87837a05725b5b5f1c22f8e9d","bugs":{"url":"https://github.com/anam-org/anam-halftone-shader/issues"},"_nodeVersion":"20.19.4","_npmVersion":"10.8.2","dist":{"integrity":"sha512-BJ14J+zeuthR1sEYJMMpKk4qzDJZ0yM+IZaHTyZq7OvcsqUIerFehpUoF1Jo+SUBQPLL7bBZn5TzIv9q8nYDQg==","shasum":"8cb8315feb4252d989e4b4280cb4c17652b87fed","tarball":"https://registry.npmjs.org/@anam-ai/halftone-shader/-/halftone-shader-0.1.3.tgz","fileCount":8,"unpackedSize":40437,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDQ5RgOVI1tvuF2FXFlX8QETZJO1y/qWzNBhE/sI4HljAIgXg7vUCiustTLaOpGSxTFGdWV5k0wTAKqet1m8tPeYns="}]},"_npmUser":{"name":"ema952","email":"ema@anam.ai"},"directories":{},"maintainers":[{"name":"ao-anam","email":"alex@anam.ai"},{"name":"benanam","email":"ben@anam.ai"},{"name":"peter-anamai","email":"peter@anam.ai"},{"name":"robbieanam","email":"robbie@anam.ai"},{"name":"sm-anam","email":"sam@anam.ai"},{"name":"smidge","email":"harry@smaje.net"},{"name":"ema952","email":"ema@anam.ai"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/halftone-shader_0.1.3_1774955741474_0.12377492797551204"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-30T12:37:27.847Z","modified":"2026-03-31T11:15:41.832Z","0.1.0":"2026-03-30T12:37:28.073Z","0.1.1":"2026-03-30T20:16:25.991Z","0.1.2":"2026-03-31T08:58:56.491Z","0.1.3":"2026-03-31T11:15:41.621Z"},"bugs":{"url":"https://github.com/anam-org/anam-halftone-shader/issues"},"author":{"name":"Ema","email":"ema@anam.ai"},"license":"MIT","homepage":"https://half-tone-shader.vercel.app","keywords":["halftone","webgl","glsl","shader","canvas","react","image-effect"],"repository":{"type":"git","url":"git+https://github.com/anam-org/anam-halftone-shader.git"},"description":"Real-time WebGL halftone effect for images. Framework-agnostic engine with a React wrapper.","maintainers":[{"name":"ao-anam","email":"alex@anam.ai"},{"name":"benanam","email":"ben@anam.ai"},{"name":"peter-anamai","email":"peter@anam.ai"},{"name":"robbieanam","email":"robbie@anam.ai"},{"name":"sm-anam","email":"sam@anam.ai"},{"name":"smidge","email":"harry@smaje.net"},{"name":"ema952","email":"ema@anam.ai"}],"readme":"# @anam-ai/halftone-shader\n\nUse this to turn any image into Anam's half tone effect.\n\nRuns entirely on the GPU via a GLSL fragment shader on a `<canvas>` element. Exports a framework-agnostic `HalftoneEngine` class for vanilla JS and a `HalftoneCanvas` React component as a thin wrapper around it.\n\n![anam halftone shader demo](https://raw.githubusercontent.com/anam-org/anam-halftone-shader/main/assets/anam-halftone-shader-demo.gif)\n\nDemo: [half-tone-shader.vercel.app](https://half-tone-shader.vercel.app)\n\n## Install\n\n```bash\nnpm install @anam-ai/halftone-shader\n```\n\n## React usage\n\nSize the wrapper with `aspectRatio` matching your image's natural dimensions. The canvas fills its parent, so the wrapper controls the layout.\n\n```tsx\nimport { HalftoneCanvas, DEFAULT_SETTINGS } from '@anam-ai/halftone-shader'\n\nfunction App() {\n  return (\n    <div style={{ width: '100%', aspectRatio: '1920 / 1080' }}>\n      <HalftoneCanvas\n        imageSrc=\"/your-image.png\"\n        settings={DEFAULT_SETTINGS}\n      />\n    </div>\n  )\n}\n```\n\n> **Note:** Update `aspectRatio` to match your image's dimensions. For a 800×600 image use `'800 / 600'`, for a square image use `'1 / 1'`, etc. If you swap the image for one with different dimensions, update the ratio to match.\n\n### Ref handle\n\nUse the ref to access the underlying canvas element or reset the animation:\n\n```tsx\nimport { useRef } from 'react'\nimport { HalftoneCanvas, HalftoneCanvasHandle, DEFAULT_SETTINGS } from '@anam-ai/halftone-shader'\n\nfunction App() {\n  const ref = useRef<HalftoneCanvasHandle>(null)\n\n  const exportFrame = () => {\n    const canvas = ref.current?.getCanvas()\n    canvas?.toBlob((blob) => { /* save blob */ }, 'image/png')\n  }\n\n  const restart = () => {\n    ref.current?.resetAnimation()\n  }\n\n  return (\n    <div style={{ width: '100%', aspectRatio: '1920 / 1080' }}>\n      <HalftoneCanvas\n        ref={ref}\n        imageSrc=\"/your-image.png\"\n        settings={DEFAULT_SETTINGS}\n      />\n    </div>\n  )\n}\n```\n\n## Next.js usage\n\nAdd `'use client'` since the shader uses browser APIs (WebGL, ResizeObserver):\n\n```tsx\n'use client'\n\nimport { HalftoneCanvas, DEFAULT_SETTINGS } from '@anam-ai/halftone-shader'\n\nexport default function Page() {\n  return (\n    <div style={{ width: '100%', aspectRatio: '1920 / 1080' }}>\n      <HalftoneCanvas\n        imageSrc=\"/your-image.png\"\n        settings={DEFAULT_SETTINGS}\n      />\n    </div>\n  )\n}\n```\n\n## Vanilla JS usage\n\nFor non-React projects, use `HalftoneEngine` directly. Size the canvas via its container element:\n\n```html\n<div style=\"width: 100%; aspect-ratio: 1920 / 1080;\">\n  <canvas id=\"my-canvas\" style=\"width: 100%; height: 100%;\"></canvas>\n</div>\n```\n\n```js\nimport { HalftoneEngine } from '@anam-ai/halftone-shader'\n\nconst canvas = document.getElementById('my-canvas')\nconst engine = new HalftoneEngine(canvas)\n\nengine.setImage('/your-image.png')\nengine.setSettings({ scale: 12, sparkleIntensity: 0.5 })\n\n// Restart the reveal/sparkle timer\nengine.resetAnimation()\n\n// Clean up when done\nengine.destroy()\n```\n\n> **Note:** Update `aspect-ratio` on the container to match your image's natural dimensions. If you swap the image for one with different dimensions, update the ratio to match.\n\n`HalftoneEngine` manages the full GL lifecycle including shader compilation, texture upload, the `requestAnimationFrame` loop, and `ResizeObserver`. Call `destroy()` to release all resources.\n\n## Settings\n\nPass a `settings` object to control the effect. Start with `DEFAULT_SETTINGS` and override what you need:\n\n```tsx\nimport { DEFAULT_SETTINGS } from '@anam-ai/halftone-shader'\n\nconst settings = {\n  ...DEFAULT_SETTINGS,\n  scale: 12,\n  sparkleIntensity: 0.5,\n  useTint: true,\n  tintColor: '#FF6200',\n}\n```\n\n| Setting | Type | Default | Description |\n|---|---|---|---|\n| `scale` | `number` | `14` | Dot grid size in pixels |\n| `gamma` | `number` | `1.5` | Controls dot size relative to image luminance |\n| `saturation` | `number` | `1` | Color saturation of dots |\n| `brightness` | `number` | `1` | Dot brightness |\n| `colorMode` | `'default' \\| 'light'` | `'default'` | `'light'` renders white dots with variable opacity |\n| `background` | `boolean` | `false` | Render a white background behind the dots |\n| `useTint` | `boolean` | `false` | Apply a single tint color to all dots |\n| `tintColor` | `string` | `'#FF6200'` | Hex or RGB color for the tint |\n| `fillPattern` | `boolean` | `false` | Fill empty (non-image) areas with an animated dot pattern |\n| `patternOpacity` | `number` | `0.5` | Opacity of the fill pattern |\n| `sparkleIntensity` | `number` | `0.4` | How strongly dots twinkle (0 = none) |\n| `sparkleSpeed` | `number` | `2` | Speed of the sparkle animation |\n| `reveal` | `boolean` | `false` | Animate dots appearing on load |\n| `revealDelay` | `number` | `0` | Seconds before reveal starts |\n| `revealDuration` | `number` | `2` | Seconds for reveal to complete |\n\n## How it works\n\nThe shader initialises a WebGL context on a `<canvas>` element and runs a custom GLSL fragment shader. Each frame:\n\n1. Divides the canvas into a grid of cells based on `scale`\n2. Samples the image colour at each cell centre\n3. Derives dot size from the luminance of that pixel\n4. Renders antialiased circles with optional sparkle animation via `requestAnimationFrame`\n\nThe image is never rasterized or pre-processed — everything is computed on the GPU in real time.\n\n## Requirements\n\n- A browser with WebGL support (all modern browsers)\n- React 18+ (only if using `HalftoneCanvas`)\n","readmeFilename":"README.md"}