All posts

Convert Images to WebP in the Browser Without Uploading

10 min read
1965 words
On this page· 6 sections

You can convert JPEG, PNG, GIF stills, BMP, and HEIC to WebP in the browser without uploading the files, because decoding and WebP encoding both run in the tab. The batch that finishes depends on this machine: file size, how many decoded frames stay in memory, and whether lossless encoding is on.

WebP Converter is that converter. People looking for convert to WebP online, JPG to WebP in the browser, PNG to WebP with no upload, HEIC to WebP, or lossless WebP get this local path: the file stays on the device. It is free, it has no watermark, and it does not ask for an account. Files are not sent to zhanghe.dev or any other server.

What the shipped tool converts

This note does not time a conversion. There is no recorded session with input bytes, output bytes, or wall-clock numbers for this tool. The figures below are the product page and the constants in the converter. Quote them as limits, not as a speed claim.

The product page turns JPEG, PNG, GIF stills, WebP, BMP, and iPhone HEIC into WebP. You can move a quality slider or switch on lossless WebP. The drop hint says up to 80 photos at once, 40MB each. The FAQ repeats the same boundary: input is JPEG, PNG, GIF stills, WebP, BMP, and iPhone HEIC/HEIF, output is WebP, animated GIFs convert the first frame only, SVG is not converted, and animated WebP is not produced.

The photo picker accept value is .jpg,.jpeg,.png,.gif,.webp,.bmp,.heic,.heif,image/jpeg,image/jpg,image/png,image/gif,image/webp,image/bmp,image/x-ms-bmp,image/heic,image/heif,image/heic-sequence,image/heif-sequence. The folder picker does not set accept. It sets webkitdirectory and directory, then the same checker filters the files. The checker accepts those extensions in any letter case, and those MIME types. .svg and .svgz are rejected before the MIME check. A rejected type shows "Only JPEG, PNG, GIF, WebP, BMP, HEIC, or HEIF files are accepted. SVG is not converted."

The guard in code is 40 * 1024 * 1024 bytes, which is 41,943,040. The on-page size formatter prints that value as 40.0 MB. The page copy says 40MB. A file of exactly 41,943,040 bytes is inside the guard. One byte over is not. An empty file fails the same check, because the guard requires a size above 0, and the page shows the same notice: "Skipped files larger than 40MB." The FAQ adds that device memory can lower the practical limit. This guide did not generate a file at that boundary.

Each accepted file becomes one WebP. photo.jpg is saved as photo.webp. vacation/IMG_001.PNG keeps the folder and becomes vacation/IMG_001.webp. A second file that would use that same path, such as vacation/IMG_001.png beside vacation/IMG_001.jpg, is named vacation/IMG_001-2.webp. A blank path becomes image.webp. ../vacation\\IMG_001.jpg is sanitized to vacation/IMG_001.jpg before the WebP rename: backslashes become slashes, and empty, ., and .. segments are dropped. A single Download uses the file name only, so vacation/IMG_001.webp downloads as IMG_001.webp. The ZIP keeps the relative path.

The list shows original size, WebP size, and pixel width and height after decode. When at least one file is Done, the totals line sums those finished files and can print smaller or larger with a percent rounded from (original total - WebP total) / original total. Those sizes are the files in the tab. This guide does not publish a sample pair.

Convert a folder without an upload

The privacy boundary is the one the FAQ states: decoding and WebP encoding run in the browser. Files are not sent to zhanghe.dev or any other server. The page loads a bundled WASM encoder, and the photo bytes are not posted. Downloads are blob: URLs created in the tab.

  1. Open WebP Converter.
  2. Drop images or a folder, choose photos, or choose a folder. A batch can include 80 photos. Past that, the page says "A batch can include 80 photos."
  3. Leave WebP quality at 80, or move the slider. The range is 50 to 95 in steps of 1. Or switch on Lossless WebP. The slider is disabled while lossless is on.
  4. Wait until each row says Done, then download one WebP or choose Download all ZIP.

The ZIP is named webp-converter.zip. It is built in the tab with fflate zipSync at level 0, which stores the WebP files instead of deflating them. Failed rows are left out. If nothing has finished, the page says "There are no WebP files to download yet." If the archive cannot be built, the page says "Could not build the ZIP."

A dropped folder is walked with webkitGetAsEntry, including nested directories. Choose folder uses the webkitdirectory input. Relative paths in the ZIP are the sanitized paths above.

Any accepted .gif shows "Animated GIFs convert the first frame only." A still GIF shows that notice too, because the checker looks at the extension and the image/gif type, not at a frame count. The worker returns one bitmap. This guide did not open a multi-frame GIF and count frames.

Quality, lossless, workers, and the decoder

The default quality is 80. Values outside 50 to 95 clamp back into that range. A non-numeric value falls back to 80. A fractional value is rounded, so 80.4 becomes 80. Moving the slider waits 120 ms, then re-encodes. Switching lossless on or off re-encodes without that wait.

Re-encode uses the decoded bitmap when it is still in memory, which is the hint under the slider: changing quality re-encodes from the decoded photo. The cache keeps at most 8 full frames: the selected photo, then the most recently finished ones. A finished frame past that cap is closed. The next quality or lossless change queues it and decodes it again. That cap is a memory limit, not a timing result. The re-encode of a cached bitmap calls the same WebP encoder in the page. This guide did not measure how long that call takes.

Lossy mode passes the clamped quality to @jsquash/webp encode with lossless 0. Lossless mode passes quality 100 and lossless 1. That 100 is the encoder argument while the switch is on. It is not the slider at 95, and the slider is disabled. The page hint says "Uses real libwebp lossless encoding, not maximum lossy quality." The FAQ says lossless mode uses bundled libwebp lossless encoding, and that lossy mode uses a 50–95 quality slider. The call sets quality and lossless only. Other encoder fields stay at the defaults shipped in @jsquash/webp 1.5.0 meta.js, including method 4. This guide did not compare those defaults with another encoder.

The installed package is @jsquash/webp 1.5.0. Its README says the codec uses libwebp. The encoder note in that package says Version: v1.0.2, which is libwebp v1.0.2. This guide did not call the encoder version() function.

Parallel work uses a worker pool. The size is hardwareConcurrency capped at 4, and at least 1. If the browser reports a missing or zero concurrency, the pool falls back to 2. The page includes a screen-reader status of that count followed by " workers". Queued photos move to an idle worker. A worker is busy while it decodes and encodes that file. This guide did not measure how the count changes wall time.

Decode tries createImageBitmap on the file blob first. If the bitmap has a width and a height above 0, the status reads "Using browser decoder". If that call fails or the bitmap is empty, and the file is HEIC or HEIF by extension or MIME, the worker imports heic-to/csp and calls heicTo({ blob, type: "bitmap" }). The status then reads "Using local WASM decoder". A batch that used both paths reads "Using browser decoder when available". The installed heic-to package is 1.5.2. Its package note says that release uses libheif 1.22.2. JPEG, PNG, GIF, WebP, and BMP do not get that WASM fallback. A file the browser decoder rejects, and that is not HEIC or HEIF, is marked Failed. The worker text for that failure is "Could not decode this image." The generic fallback, when the thrown value is not an Error, is "Could not convert this image."

Limits for size, metadata, and format

  • Size. The page says 40MB each and 80 photos. The byte guard is 41,943,040. Under the guard is not a promise that a large still will finish. The FAQ already points at device memory.
  • Batch. Eighty is the slot cap. Extra files are skipped with the batch message.
  • Folder. Drops and the folder picker walk nested directories. The ZIP keeps the relative path. A single Download strips the directory and saves the file name.
  • Memory. Eight decoded frames stay. The rest are dropped and decoded again if you move the quality slider or switch lossless. The worker pool does not raise that cache.
  • Metadata. The FAQ says orientation is applied to the pixels, and that date, GPS, and other capture fields are not written into the WebP. The badge says "Capture data and GPS removed". That is the default, not a toggle. The encoder draws the bitmap, reads ImageData, and calls encode. There is no EXIF copy step in that function. This guide did not open a WebP in an EXIF reader.
  • GIF and animation. The page says animated GIFs convert the first frame only, and that animated WebP is not produced. The encoder runs once per bitmap. This guide did not count frames.
  • SVG. .svg and .svgz are rejected. The notice says SVG is not converted.
  • WebP input. An existing .webp file is accepted and encoded again. It is not copied through as the same bytes. This guide did not compare the input and output bytes.
  • Decoder. Native decode is whatever createImageBitmap accepts in this browser. The WASM path is the HEIC and HEIF fallback. A file both applicable paths reject is marked Failed.
  • Not a cloud converter. Nothing in this tool posts the photo. The FAQ says the page loads a bundled WASM encoder, and that images never leave this device. A later tool on this site can upload for a different job. That upload is not this conversion.

Thumbnails in the list are a separate 160-pixel-wide JPEG encoded at quality 0.7. They are previews, not the download.

Two other local photo tools

HEIC to JPG turns iPhone HEIC and HEIF stills into JPEG. Its page allows 40 photos at 40MB each, a JPEG quality slider from 40 to 95 with a default of 85, and a ZIP named heic-to-jpg.zip. It does not take a folder, and it does not offer lossless. Use it when the output should be JPEG. WebP Converter writes WebP, including lossless and a folder batch.

Cover Moment builds a social cover in the browser from a photo and a hook title. The page says processing and PNG export run locally, and the export is a PNG at the selected platform size, for example 1280×720 for YouTube. The file picker uses accept="image/*". It is not this WebP batch converter. This guide did not load a converted WebP into Cover Moment.

Convert the stills on this device

To turn JPEG, PNG, GIF stills, BMP, WebP, or iPhone HEIC into WebP without an upload, open WebP Converter, set the quality or switch on lossless, and download the WebP or the ZIP. For a JPEG from an iPhone HEIC still, use HEIC to JPG. For a local social cover exported as PNG, use Cover Moment.

Related articles