blurhash.wrapuj.to

BlurHash as a service

Image in.28 characters out.

The backdrop above is not a photograph. It is the string below, decoded by this API's /decode endpoint — small enough to sit in a database column next to the image URL, and to arrive with your JSON instead of after it.

LEHV6nWB2yk8pyo0adR*.7kCMdnj
1 char · grid
4 × 3 components
1 char · contrast
scale of the detail terms
4 chars · base colour
the image's average
22 chars · detail
11 cosine terms, 2 chars each

Try it

Runs against this API, in your browser.

or

Result Your image and its hash, side by side,
with the timings the server measured.
The image you supplied
Source
The BlurHash, decoded back into a placeholder
The hash, decoded

Endpoints

No key, no signup. CORS is open.

POST /blurhash

Send the image as the raw request body. This is the fastest path — nothing to parse before the decoder sees the bytes.

# the response is JSON on stdout
curl -X POST --data-binary @photo.jpg https://blurhash.wrapuj.to/blurhash

POST /blurhash multipart

Or post a form. The file field may be named image, file or upload.

curl -F [email protected] https://blurhash.wrapuj.to/blurhash

GET /blurhash?url=

Hand over a URL and the API fetches the image itself. Results are cached for an hour, so a repeat call answers from memory. Only public addresses are reachable.

curl 'https://blurhash.wrapuj.to/blurhash?url=https://example.com/photo.jpg'

GET /decode?hash=

The inverse: a hash back into a PNG placeholder, for previews and debugging. width, height and punch are optional.

curl 'https://blurhash.wrapuj.to/decode?hash=LEHV6nWB2yk8pyo0adR*.7kCMdnj&width=320' > blur.png

Response

Every hashing call answers with the same shape. width and height describe the original image, so you can set the placeholder's aspect ratio without a second request.

{
  "blurhash": "LEHV6nWB2yk8pyo0adR*.7kCMdnj",
  "width": 4032,
  "height": 3024,
  "components": { "x": 4, "y": 3 },
  "format": "jpeg",
  "bytes": 2847193,
  "sampled": { "width": 100, "height": 75 },
  "timing_ms": { "decode": 3.1, "encode": 0.4 }
}

Errors

Failures come back as JSON with a stable code and a message meant to be read by a person.

{ "error": { "code": "unsupported_format",
            "message": "GIF is not supported; send JPEG, PNG or WebP" } }

What it does

And what it deliberately does not.

Formats
JPEG, PNG, WebP. The container is read from magic bytes, not from the Content-Type you send.
Speed
JPEGs are decoded through the DCT scaler, so a 12 MP photo never reaches full resolution in memory.
Components
1–9 on each axis via x and y. Defaults to 4 × 3, the size the reference recommends.
Transparency
Alpha is dropped, not composited — matching the reference encoders. A hash cannot carry transparency.
Upload limit
25 MB per image, and at most 80 megapixels before scaling.
Compatibility
Output is byte-identical to woltapp/blurhash, so any client decoder reads it.