npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@anam-ai/halftone-shader

v0.1.3

Published

Real-time WebGL halftone effect for images. Framework-agnostic engine with a React wrapper.

Downloads

332

Readme

@anam-ai/halftone-shader

Use this to turn any image into Anam's half tone effect.

Runs 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.

anam halftone shader demo

Demo: half-tone-shader.vercel.app

Install

npm install @anam-ai/halftone-shader

React usage

Size the wrapper with aspectRatio matching your image's natural dimensions. The canvas fills its parent, so the wrapper controls the layout.

import { HalftoneCanvas, DEFAULT_SETTINGS } from '@anam-ai/halftone-shader'

function App() {
  return (
    <div style={{ width: '100%', aspectRatio: '1920 / 1080' }}>
      <HalftoneCanvas
        imageSrc="/your-image.png"
        settings={DEFAULT_SETTINGS}
      />
    </div>
  )
}

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.

Ref handle

Use the ref to access the underlying canvas element or reset the animation:

import { useRef } from 'react'
import { HalftoneCanvas, HalftoneCanvasHandle, DEFAULT_SETTINGS } from '@anam-ai/halftone-shader'

function App() {
  const ref = useRef<HalftoneCanvasHandle>(null)

  const exportFrame = () => {
    const canvas = ref.current?.getCanvas()
    canvas?.toBlob((blob) => { /* save blob */ }, 'image/png')
  }

  const restart = () => {
    ref.current?.resetAnimation()
  }

  return (
    <div style={{ width: '100%', aspectRatio: '1920 / 1080' }}>
      <HalftoneCanvas
        ref={ref}
        imageSrc="/your-image.png"
        settings={DEFAULT_SETTINGS}
      />
    </div>
  )
}

Next.js usage

Add 'use client' since the shader uses browser APIs (WebGL, ResizeObserver):

'use client'

import { HalftoneCanvas, DEFAULT_SETTINGS } from '@anam-ai/halftone-shader'

export default function Page() {
  return (
    <div style={{ width: '100%', aspectRatio: '1920 / 1080' }}>
      <HalftoneCanvas
        imageSrc="/your-image.png"
        settings={DEFAULT_SETTINGS}
      />
    </div>
  )
}

Vanilla JS usage

For non-React projects, use HalftoneEngine directly. Size the canvas via its container element:

<div style="width: 100%; aspect-ratio: 1920 / 1080;">
  <canvas id="my-canvas" style="width: 100%; height: 100%;"></canvas>
</div>
import { HalftoneEngine } from '@anam-ai/halftone-shader'

const canvas = document.getElementById('my-canvas')
const engine = new HalftoneEngine(canvas)

engine.setImage('/your-image.png')
engine.setSettings({ scale: 12, sparkleIntensity: 0.5 })

// Restart the reveal/sparkle timer
engine.resetAnimation()

// Clean up when done
engine.destroy()

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.

HalftoneEngine manages the full GL lifecycle including shader compilation, texture upload, the requestAnimationFrame loop, and ResizeObserver. Call destroy() to release all resources.

Settings

Pass a settings object to control the effect. Start with DEFAULT_SETTINGS and override what you need:

import { DEFAULT_SETTINGS } from '@anam-ai/halftone-shader'

const settings = {
  ...DEFAULT_SETTINGS,
  scale: 12,
  sparkleIntensity: 0.5,
  useTint: true,
  tintColor: '#FF6200',
}

| Setting | Type | Default | Description | |---|---|---|---| | scale | number | 14 | Dot grid size in pixels | | gamma | number | 1.5 | Controls dot size relative to image luminance | | saturation | number | 1 | Color saturation of dots | | brightness | number | 1 | Dot brightness | | colorMode | 'default' \| 'light' | 'default' | 'light' renders white dots with variable opacity | | background | boolean | false | Render a white background behind the dots | | useTint | boolean | false | Apply a single tint color to all dots | | tintColor | string | '#FF6200' | Hex or RGB color for the tint | | fillPattern | boolean | false | Fill empty (non-image) areas with an animated dot pattern | | patternOpacity | number | 0.5 | Opacity of the fill pattern | | sparkleIntensity | number | 0.4 | How strongly dots twinkle (0 = none) | | sparkleSpeed | number | 2 | Speed of the sparkle animation | | reveal | boolean | false | Animate dots appearing on load | | revealDelay | number | 0 | Seconds before reveal starts | | revealDuration | number | 2 | Seconds for reveal to complete |

How it works

The shader initialises a WebGL context on a <canvas> element and runs a custom GLSL fragment shader. Each frame:

  1. Divides the canvas into a grid of cells based on scale
  2. Samples the image colour at each cell centre
  3. Derives dot size from the luminance of that pixel
  4. Renders antialiased circles with optional sparkle animation via requestAnimationFrame

The image is never rasterized or pre-processed — everything is computed on the GPU in real time.

Requirements

  • A browser with WebGL support (all modern browsers)
  • React 18+ (only if using HalftoneCanvas)