Skip to content

How to turn one image into a BlurHash string and a JPEG LQIP

The workbench reads one local image with the browser’s own decoder, paints it on a canvas at no more than 160 px on the longest edge, and encodes those pixels twice: once as a BlurHash string, once as a small JPEG data URL. Both are placeholders for the same picture, and neither leaves the tab.

The left preview is the hash decoded again at the shape of the source, and the right one is the JPEG that was just built, so the two boxes show what a visitor sees while the real image loads. The line underneath reports the source size, the length of the string and the weight of the JPEG.

  1. Choose a local image. Any format the browser can decode works — JPEG, PNG, WebP — and an animated file is read as its first frame; the 120 × 60 test GIF produced a red placeholder from its first frame. The status line confirms the file name once the picture is ready.
  2. Set the two BlurHash component counts, X for columns and Y for rows, 1 to 9 each, 4 × 3 by default. More components keep more detail and lengthen the string; a value outside the range is pulled back to 9 or 1, and an empty field returns to its default.
  3. Set the LQIP width, 8 to 96 px, 32 by default. It is the pixel width of the JPEG, not a display size: the height follows the source ratio, and the file stays in the low kilobytes at every width the field allows.
  4. Press Generate placeholders. The decoded hash and the JPEG appear side by side, the meta line reports the numbers, and a transparent image is flattened onto white in both outputs.
  5. Take the result out with Copy BlurHash, Copy LQIP URL or Download LQIP — the file is saved as voritools-lqip.jpg. Clear empties the picker and both outputs and returns the fields to 4 × 3 and 32.

What the string, the JPEG and the two previews contain

What the BlurHash string contains

A BlurHash is a short text encoding of a blurred version of the picture. The first characters carry the component grid and a quantised maximum, then the average colour and one coefficient per grid cell follow. With the default 4 × 3 grid the string measured 28 characters; a single 1 × 1 component produced 6 characters and a flat colour, while the maximum 9 × 9 produced 166 characters and keeps the most structure.

The string is meant for a BlurHash decoder in your own page: the reference JavaScript, PHP, Swift or Kotlin libraries, the React component, or an image CDN that accepts a hash. It carries no width, height or metadata — the decoder draws it at whatever size you ask for. This page encodes, and it also decodes its own hash for the preview, but it does not accept a hash as input.

The LQIP JPEG and its data URL

The second output is a real JPEG, written at the width you chose and a fixed compression level (quality 0.58). For the 800 × 500 sample used in testing, the default 32 px gave a data URL of 987 bytes — 1 KB in the meta line — and 96 px stayed under 1.6 KB. The meta line prints the exact figure for your image.

Because it is a data URL it can be pasted straight into an img src, a CSS background-image or a picture element without a second request. JPEG has no alpha channel, so transparent areas are painted white — the same flattening the hash is built from — and the file keeps the aspect ratio of the source. Download saves it as voritools-lqip.jpg.

Local processing, accuracy and limits

The image is read, painted and encoded inside the page. Nothing about the file is sent anywhere, there is no account and no upload step, and the tool keeps working with the network disconnected once the page has loaded. The output is reproducible too: the same image with the same component counts and width always produces the same string and the same JPEG.

The encoder implements the reference BlurHash algorithm; hashes produced here were compared against the reference implementation and matched, so another decoder renders the same placeholder. The only real limits come from the browser: a very large photo is fine because the encoding samples at 160 px, but the tab still has to decode the file first, and a format the browser cannot read has to be converted before it can be used here.

Recent tools: