Skip to content

Reference ​

Architecture ​

Three npm packages share one core. hazehash holds the whole codec: an encoder that downsamples the image in linear light, converts it to OKLab and describes it with separate luma and chroma DCT grids, picks the quantisation with a rate-distortion search that fits the byte budget, and writes the coefficients with Golomb–Rice coding, and a decoder that does the reverse. It also contains the thin platform layers: hazehash/canvas (draws into a canvas), hazehash/node (reads image files through the optional peer sharp) and the hazehash command. The core files never touch window, document, Buffer, eval or dynamic import(), and a test enforces it.

hazehash-vue is a small layer on top of the decoder: a usePlaceholder() composable that holds the colour, the aspect ratio and a canvas ref, and a PlaceholderImage component built on it. hazehash-nuxt registers that component, scans your image folders at build time with hazehash/node, caches the hashes by file size and modification time, and exposes them to the component as a virtual module.

The format of a hash is documented on Hash Format, and the choices behind it are measured on Benchmark.

SSR compatibility ​

The decoder and the encoder are plain functions without environment access, so they run on a server. What needs a browser is drawing into a canvas. The Vue component and the composable are written for server rendering: on the server they produce only background-color (the average colour) and aspect-ratio, and the canvas is added after mount, so the server markup and the first client render are identical and hydration cannot mismatch. No <ClientOnly> wrapper is needed, in Nuxt or elsewhere.

Accessibility ​

The canvas with the blurred preview is decorative and is marked aria-hidden="true", so assistive technology reads only the real image and its alt text. The fade-in of the real image is turned off when the visitor prefers reduced motion (prefers-reduced-motion: reduce).

Bundle size & peer dependencies ​

The sizes are minified and gzipped, and include the shared code each entry point imports:

  • hazehash and hazehash/decode — about 2.6 KB
  • hazehash/encode — about 6.3 KB
  • hazehash/canvas — about 0.05 KB on top of the decoder

Every package ships sideEffects: false, so a bundler keeps only what you import. hazehash has no runtime dependencies and one optional peer, sharp (>=0.33), used only by hazehash/node, the command line and the Nuxt module's build-time generation. hazehash-vue has the peers hazehash and vue (^3.3.0). hazehash-nuxt depends on @nuxt/kit, hazehash and hazehash-vue, and has the peers nuxt (^3.9.0) and the optional sharp. All three support Node.js 20 and newer.

Comparison ​

HazeHash does the same job as BlurHash and ThumbHash: a short string that stands in for an image until it loads.

  • Quality — at the same size HazeHash has a mean perceptual error about 49% lower than BlurHash and 20% lower than ThumbHash, and at 20 bytes, the size of a ThumbHash, it is still 14% better. See Benchmark.
  • Size — a budget from 16 to 48 bytes, 28 by default, against ThumbHash's native output of about 21 bytes and BlurHash's strings, which grow with the grid.
  • Transparency — the alpha channel is stored only when the image needs it.
  • Aspect ratio — stored in the header, so the box can be sized from the hash alone.
  • Format — a new format, not compatible with either of them; a HazeHash cannot be decoded as a BlurHash or a ThumbHash and the other way round. Existing hashes have to be created again from the source images.

Development ​

A pnpm workspace with packages/core, packages/vue and packages/nuxt, plus a Nuxt demo, benchmark scripts, examples and end-to-end tests. Building needs Node.js 22 and pnpm.

bash
pnpm install
pnpm build       # all packages
pnpm test        # builds, then the unit tests of core, vue and nuxt
pnpm typecheck
pnpm lint
pnpm e2e         # Chromium, Firefox, WebKit, Cloudflare Workers runtime, Nuxt SSR
pnpm demo        # a Nuxt app with 17 images next to their placeholders
pnpm bench       # the full benchmark

The core package also has size (bundle sizes), fuzz and make-vectors (the frozen conformance vectors, which refuses to overwrite them).

License ​

MIT.