BlurHash Generator

Generate a BlurHash placeholder string from any image. Compact ~30-byte hash that previews as a blurred image — perfect for image loading placeholders on Next.js, Unsplash-style feeds, and SSR.

Loading…

All processing runs in your browser — no files or inputs are uploaded to a server.

How to use

Drop an image into the file picker — JPEG, PNG, WebP, GIF, AVIF all work because the browser decodes via the `<img>` element. The tool downscales the image to 64 px on the long side, runs Wolt's `blurhash` encoder with the chosen X/Y component counts (default 4×3), and prints the resulting ~28-character string plus a 256-px decoded preview canvas. Higher component counts capture finer detail at the cost of a longer hash: a 4×3 hash is 28 chars, a 9×9 hash is 67 chars; 4×3 is the published Unsplash default and almost always enough.

The encoded BlurHash is a base83 string that fits in a database column, an API response, or a JSX prop. On the rendering side, decode at the actual placeholder size with the same `blurhash` library (or `react-blurhash` for a drop-in component) and paint the result into a `<canvas>` or use it as `placeholder="blur"` in Next.js' Image component. Trade-offs versus alternatives: a raw 64-px thumbnail is 1–4 KB and looks identical, but a BlurHash is 30 bytes and fits in the same JSON response as the rest of the image metadata, which matters once you're shipping 100+ thumbnails per page. The encoder runs entirely in your browser — the image data never reaches a network endpoint.

Examples

Unsplash-style 4×3 default

Input
image:        landscape-photo.jpg (4032 × 3024 px, 2.8 MB)
encode:       4 × 3 components (default)
use case:     general-purpose blur placeholder
Output
hash:         LEHLk~WB2yk8pyo0adR*.7kCMdnj
hash length:  28 characters
bytes:        30 (UTF-8) — fits in a single PostgreSQL TEXT cell
decoded preview: 256 × 192 canvas, looks like a heavily blurred version of the input

4×3 is Unsplash's production setting and the original BlurHash blog post's recommended default. The first character (`L`) encodes the number of components; the next two encode the maximum AC component (used for normalization); the rest encodes the DC component (average color) and the AC components (gradient terms) for each channel. A 4×3 hash captures the overall shape and dominant color of the image well enough that users perceive instant feedback while the full-res image streams in. The hash is locale-invariant — feeding the same image on macOS and Linux produces byte-identical output.

High-detail 9×9 portrait hash

Input
image:        portrait.jpg (face occupies center 60% of frame)
encode:       9 × 9 components
use case:     profile photo blur — face shape should be discernible
Output
hash:         LKO2?V%2Tw=w]~RBVZRi};RPxuwH%3t8WC0L0~Lo~SRP$|JE,N9d%6gP0ZJ%R
hash length:  67 characters
bytes:        69
perceptual quality: skin tone, hair shape, background color all distinguishable
encoder time: ~120 ms in browser (vs. ~5 ms for 4×3)

For portraits, profile photos, and product hero shots where you want viewers to perceive the subject before the full image loads, push components up to 6×6 or 9×9. Cost: encoder time is O(width × height × components_x × components_y), so a 9×9 hash takes ~30× longer to encode than 4×3 — still under a second for any sensible input size, but worth knowing if you batch-encode thousands of images server-side. Max components is 9×9 per the BlurHash spec; the encoder accepts higher values but the result is no more informative because the input is downscaled to 32 × 32 internally.

Mostly-uniform background — minimum 1×1

Input
image:        solid-blue-sky.jpg (almost no detail)
encode:       1 × 1 components
use case:     placeholder for known-uniform images (sky, gradient, brand backdrop)
Output
hash:         00OG^x
hash length:  6 characters
bytes:        6
decoded preview: single solid color matching the image's average
encoder time: <1 ms

1×1 reduces BlurHash to just the DC component — the image's average color. The hash is 6 characters and the result is indistinguishable from a CSS solid `background-color`. Useful for placeholders where you only need to reserve layout space with a representative color while waiting for the real image. The browser hint `<img style="background-color: #...">` does the same thing for half the cost, but BlurHash centralizes the color in your existing image metadata pipeline. For images with any meaningful structure (logos, products, faces, landscapes), use at least 3×3.

FAQ

Who created BlurHash and why?

BlurHash was published in 2018 by **Dag Ågren**, a Finnish engineer at **Wolt** (food delivery, later acquired by DoorDash). Wolt's mobile app needed image placeholders during list scrolling — when users browse a restaurant menu on slow mobile data, the blank gray boxes where photos load felt jarring. They tried solid average-color blocks (too simple), tiny pre-decoded thumbnails (too big in JSON payloads), and SQIP / Polished-style SVG approximations (too fragile). BlurHash split the difference: ~30 bytes that decode to a recognizable blur. The reference implementation is C with TypeScript, Swift, Kotlin, Python, Go, and Rust ports; the algorithm itself is a small Discrete Cosine Transform packed into base83. It is **MIT-licensed** so you can ship it in commercial apps freely.

How does BlurHash actually compress an image to 30 bytes?

It is a **Discrete Cosine Transform** (DCT) — the same math at the heart of JPEG, but with the high-frequency coefficients aggressively truncated. The encoder downscales the input image, computes the DCT of the small image, keeps only the components_x × components_y lowest-frequency coefficients in each RGB channel, and packs the values as base83 ASCII. Decoding is the inverse: read coefficients, IDCT, render to canvas. The compression ratio comes from throwing away ~99% of the DCT coefficients — the result has no detail beyond a few sweeping color gradients, but those gradients are usually what carries the "feel" of an image at small sizes. The encoder uses linear-light sRGB internally to make color blending look natural; the decoder converts back to sRGB before rendering.

BlurHash vs LQIP / SQIP / ThumbHash — which should I use?

**LQIP** (Low-Quality Image Placeholder) is just a downscaled JPEG at 5–10% quality, typically 16–32 px on the long side. Inline-encoded as a data URI, it costs 200–500 bytes — bigger than BlurHash but renders without JavaScript and is supported natively by Next.js' `placeholder="blur"`. **SQIP** generates SVG primitives that approximate the image; visually distinctive but 1–3 KB and slow to encode. **ThumbHash** (2023, by Evan Wallace of Figma) is the modern competitor to BlurHash — ~25 bytes, slightly better visual quality, supports transparency, and decodes faster. Use **BlurHash** when you need maximum ecosystem support (every major language has a port, integrates with Unsplash). Use **ThumbHash** for new projects starting today — same idea, slightly better trade-offs, but the ecosystem is younger. Use **LQIP** if you do not run JavaScript on the render path or if your CDN already optimizes images.

How do I use a BlurHash with Next.js or React?

In React, the simplest path is the **`react-blurhash`** package: `<Blurhash hash={hash} width={400} height={300} />` renders a canvas client-side. For server-rendered React, decode the hash to RGBA pixels server-side and emit a base64 data URL, then use it as a `<img src=...>` or as a CSS `background-image` — this is what Next.js' `placeholder="blur"` does internally for static images. To plug BlurHash into Next.js Image, decode the hash to a data URL during the page's `getStaticProps` / Server Component fetch, pass it as `blurDataURL`, and set `placeholder="blur"`. Note that Next.js' built-in blur expects a base64 JPEG/PNG data URL, not a BlurHash string — you must decode first. Vue, Svelte, and Solid all have analogous packages: `vue-blurhash`, `svelte-blurhash`, `solid-blurhash`. Plain HTML works too: load the `blurhash` npm package, decode to ImageData, paint to canvas.

Can I encode BlurHash on the server instead of in the browser?

Yes, and you usually should for production. The Node.js `blurhash` package is the same library this tool uses, just running outside a browser; combine it with `sharp` to decode any image format and downscale before encoding. Python: `blurhash-python`. Go: `github.com/buckket/go-blurhash`. Rust: `blurhash` crate. The typical pipeline is: image uploaded → `sharp({raw}).resize(64).raw().toBuffer()` → `encode(buffer, 64, 64*h/w, 4, 3)` → store hash in DB next to the image URL. The browser tool here is for ad-hoc one-off encoding, design exploration of component counts, or when you do not control the image upload pipeline. Server-side encoding also lets you index the hash for similarity queries — the DC components alone act as a tiny perceptual hash for "find images with similar dominant color".

Why does my decoded preview look more saturated than the original?

BlurHash is encoded in linear sRGB color space — the algorithm averages and DCT-transforms colors *after* removing the gamma curve, so it gets perceptually correct color mixing. When you ask the decoder for a `punch` value greater than 1 (the default in `react-blurhash` is 1.0, but some integrations default to 1.5), it amplifies the AC components, making colors more vivid than the original. Decode with `punch: 1.0` for faithful reproduction; use higher punch only when you want the placeholder to feel "alive" before the real image arrives, accepting that there will be a brief color shift at swap time. Some BlurHash integrations also miss the linear-light correction and produce washed-out output instead — verify against the reference implementation if your colors look off.

Related concepts

BlurHash sits in a category of techniques called **placeholder images** or **progressive image loading**, which trace back to interlaced GIF / JPEG progressive decoding (Marc Adler's 1986 work for CompuServe; the JFIF progressive baseline from 1994). The shared goal: show the viewer *something* immediately so the page does not appear to be loading nothing, and let detail arrive over time. The modern web revisited the idea around 2014 when **Medium** published their now-famous blog post on using a tiny embedded thumbnail to drive a CSS blur filter — this is what we now call **LQIP**. From there: Cloudinary's **vectorized SVG** placeholders (2016), Tobias Baldauf's **SQIP** (2017), Wolt's **BlurHash** (2018), Figma engineer Evan Wallace's **ThumbHash** (2023). Each iteration trades bytes against perceptual quality and ecosystem maturity. The 2024-onward generation includes **AVIF / WebP progressive loading**, which lets the browser render a low-detail version of the same file before the full image arrives.

The underlying math is the **Discrete Cosine Transform** introduced in 1974 by Nasir Ahmed, the same transform that powers JPEG, MP3, MPEG, and H.264 video. DCT separates spatial frequencies: low-frequency components capture broad gradients and color shifts, high-frequency components capture sharp edges and fine textures. Throwing away high frequencies degrades the image gracefully — humans perceive low-frequency content much more strongly. BlurHash takes this to its logical extreme by keeping only 9–81 DCT coefficients per RGB channel and packing them tightly. JPEG keeps thousands. The same insight underlies thumbnail generation and image hashing techniques used in reverse-image-search engines (perceptual hashes like pHash and dHash truncate DCT coefficients similarly).

Three adjacent concerns intersect with BlurHash. **Cumulative Layout Shift (CLS)** is a Core Web Vitals metric that penalizes pages where elements move around as content loads — image placeholders prevent CLS by reserving exact pixel space. BlurHash works well here because the placeholder occupies the same width × height as the eventual image. **Accessibility**: a BlurHash placeholder by itself has no alt text or semantic content; pair it with `<img alt="...">` and ensure screen readers see the alt rather than the canvas. **Content Security Policy**: BlurHash decoding uses `<canvas>` and inline base64 data URIs, both of which can be restricted by strict CSP `script-src` or `img-src` rules — check that your `default-src` allows `data:` URIs if you serve decoded BlurHash thumbnails as data URLs. None of these blocks BlurHash adoption; they are operational details to verify during integration.

Related tools