Disclosure: This report on the image-processing tests run by the LighterPics project was written with AI assistance.

Drawing a photo to a canvas and exporting it again is a common way to remove EXIF. The tests here inspected 3,586 outputs from browser image-processing pipelines. The parser found no GPS coordinates, camera make or model, capture date, XMP or IPTC entries. Every WebKit output still contained an EXIF block.

The test set was small: 25 JPEG photos, one generated test JPEG, and 10 generated PNGs used only in Chromium. The 3,586 files came from applying different operations and settings to those inputs, not from 3,586 distinct photos. Only three of the photos and the test JPEG contained GPS coordinates.

Metadata found in the output

Field or block detected Chromium 145.0.7632.6 Firefox 146.0.1 WebKit 26.0
GPS coordinates 0 of 3,198 0 of 207 0 of 181
Camera make or model 0 of 3,198 0 of 207 0 of 181
Capture date (DateTimeOriginal) 0 of 3,198 0 of 207 0 of 181
XMP 0 of 3,198 0 of 207 0 of 181
IPTC entries 0 of 3,198 0 of 207 0 of 181
Any EXIF block 0 of 3,198 0 of 207 181 of 181
ICC colour profile 2,451 of 3,198 0 of 207 0 of 181

These are counts of output files inspected, not metadata-removal rates for inputs that carried each field. Chromium also ran a broader set of settings. Its totals include MozJPEG and OxiPNG WebAssembly encoders, so the aggregate is not a pure comparison of canvas encoders. The format breakdown below separates those paths.

The engine versions in the table are headless Playwright builds tested on macOS.

WebKit wrote a new EXIF block

All 181 WebKit outputs contained EXIF: an APP1 segment in JPEG and an eXIf chunk in PNG. The tag set was the same in every file:

  • IFD0 contained only the pointer to the Exif IFD (tag 34665).
  • The Exif IFD contained ColorSpace (40961), PixelXDimension (40962) and PixelYDimension (40963).

These are the tag names defined by CIPA DC-008. The PNG specification defines the eXIf chunk. [1] [2]

The block also appeared in all 14 outputs made from the two photos with no input EXIF. Every WebKit JPEG contained an APP13 segment labelled Photoshop 3.0, but the parser found no IPTC datasets in those segments.

An EXIF-presence check would therefore flag every WebKit output, including files whose inputs had no EXIF. A check for the fields of concern is more informative.

Colour metadata varied by encoder

Chromium wrote an ICC profile into every canvas-encoded JPEG and every WebP output. That included 1,314 outputs made from inputs with no ICC profile. Its canvas PNGs contained no colour-profile chunk, and neither did its WebAssembly outputs.

Firefox wrote no ICC profile in any tested format. WebKit wrote an sRGB chunk in PNG, but no ICC profile in JPEG or PNG.

The HTML standard explicitly covers resolution metadata and colour profiles when serializing a canvas bitmap. It is not a promise that the resulting file will contain no metadata. [3]

The harness detected the presence of ICC profiles without decoding their contents. These results therefore do not establish whether an input profile was preserved or replaced.

File structure by encoder and format

For JPEG, this table lists APPn, SOF and SOS markers only. PNG and WebP entries list chunk types in order.

Engine Output path Markers or chunks Files
Chromium JPEG, canvas APP0 JFIF, APP2 ICC profile, SOF0, SOS 1,587
Chromium JPEG, MozJPEG WebAssembly APP0 JFIF, SOF2, SOS 441
Chromium WebP VP8X, ICCP, then VP8 (713), ALPH + VP8 (115) or VP8L (36) 864
Chromium PNG, canvas and OxiPNG IHDR, IDAT, IEND 306
Firefox JPEG APP0 JFIF, SOF0, SOS 155
Firefox WebP VP8 26
Firefox PNG IHDR, IDAT, IEND 26
WebKit JPEG APP0 JFIF, APP1 Exif, APP13 Photoshop 3.0, SOF0, SOS 155
WebKit PNG IHDR, sRGB, eXIf, IDAT, IEND 26

Of the 306 Chromium PNGs, 296 used the canvas encoder. No output contained a JPEG COM segment or a PNG text chunk.


The orientation tag was gone; the dimensions were rotated

The generated test JPEG had 1600×1200 stored pixels and EXIF Orientation 6. Every converter output from it was 1200×1600, with no Orientation tag: 21 outputs in Chromium, three in Firefox and two in WebKit.

The converter used createImageBitmap(blob, {imageOrientation: "from-image"}). The output dimensions are consistent with applying the input orientation before encoding. "from-image" is also the default in the ImageBitmap options. [4]

A WebP request could return PNG

The application rejected all 26 WebKit conversion attempts that requested WebP because the returned blob was not WebP. In the single-photo snippet check below, the returned type was image/png.

Canvas export falls back to PNG when the requested format is unsupported. Check the returned blob.type before assigning an extension, or a .webp download may contain PNG data. [5]

Test setup and limits

The harness built LighterPics locally and called the same processing functions and Web Workers as the tool pages. The canvas paths decoded with createImageBitmap(), drew to a canvas or OffscreenCanvas, and exported with toBlob() or convertToBlob(). Two Chromium compressor modes used MozJPEG and OxiPNG WebAssembly encoders instead.

The runs used Playwright 1.58.2 on an Apple M3 Pro, macOS Darwin 25.6.0, arm64. Playwright supplies the browser builds; these were not tests of the corresponding branded releases. [6]

The 25 CC0 JPEG photos came from Wikimedia Commons and were SHA-256 checked before the run. EXIF was present in 23 photos; GPS coordinates in three; camera make or model in 15; a capture date in 16; XMP in 19; IPTC entries in 15; and an ICC profile in 14. The generated JPEG added a known orientation, camera fields, capture date, GPS coordinates and an ICC profile. Chromium also processed five generated screenshots and five generated graphics, all PNGs.

Operations covered compression, conversion, resize, crop, rotation, flipping and watermarking. Chromium ran the full settings matrix; Firefox and WebKit ran a smaller selection of default settings. The parser inspected JPEG markers, PNG and WebP chunks, EXIF tag numbers and IPTC dataset counts. Per-tool summaries are available on the project page. [7]

The scope is limited to these inputs and builds. HEIC, TIFF and PNGs carrying text metadata were not part of the cross-engine photo set. The checks did not inspect ICC contents or look for information in the pixels, such as visible text or steganography. They do not establish that arbitrary input files will be free of personal information after re-encoding.

Inspect a file in your browser

This snippet lists container blocks for one of the test photos before and after export. It does not parse the individual EXIF fields, so it cannot reproduce the full privacy check on its own.

Open about:blank in a desktop browser and paste it into the DevTools console. The Wikimedia file server allows the cross-origin request.

(async () => { // Lists JPEG marker segments (up to SOS), PNG chunks or WebP chunks, in file order. function listBlocks(b) { const out = [], u32 = i => (b[i] << 24 | b[i + 1] << 16 | b[i + 2] << 8 | b[i + 3]) >>> 0; if (b[0] === 0xff && b[1] === 0xd8) { // JPEG: marker segments up to SOS for (let i = 2; i + 4 <= b.length && b[i] === 0xff;) { const m = b[i + 1], len = b[i + 2] << 8 | b[i + 3]; let name = m >= 0xe0 && m <= 0xef ? "APP" + (m - 0xe0) : m === 0xfe ? "COM" : m === 0xdb ? "DQT" : m === 0xc4 ? "DHT" : m === 0xdd ? "DRI" : m === 0xda ? "SOS" : m >= 0xc0 && m <= 0xcf ? "SOF" + (m - 0xc0) : "0x" + m.toString(16); if (name.startsWith("APP")) { // APPn segments start with an ID string let id = ""; for (let j = i + 4; j < i + 36 && b[j]; j++) id += String.fromCharCode(b[j]); name += ":" + id; } out.push(name); if (m === 0xda) break; // compressed image data follows i += 2 + len; } } else if (b[0] === 0x89 && b[1] === 0x50) { // PNG: chunk types for (let i = 8; i + 8 <= b.length;) { const type = String.fromCharCode(b[i + 4], b[i + 5], b[i + 6], b[i + 7]); out.push(type); if (type === "IEND") break; i += 12 + u32(i); } } else if (b[0] === 0x52 && b[8] === 0x57) { // "RIFF....WEBP": chunk FourCCs for (let i = 12; i + 8 <= b.length;) { const size = (b[i + 4] | b[i + 5] << 8 | b[i + 6] << 16 | b[i + 7] << 24) >>> 0; out.push(String.fromCharCode(b[i], b[i + 1], b[i + 2], b[i + 3])); i += 8 + size + (size & 1); } } const runs = []; // "IDAT IDAT IDAT" -> "IDAT×3" for (const name of out) { const last = runs[runs.length - 1]; if (last && last.name === name) last.count++; else runs.push({name, count: 1}); } return runs.map(r => r.count > 1 ? r.name + "×" + r.count : r.name); } const bytes = async blob => new Uint8Array(await blob.arrayBuffer()); const src = await (await fetch("https://upload.wikimedia.org/wikipedia/commons/0/0f/Star_night_%28Unsplash%29.jpg")).blob(); console.log("input", src.size, listBlocks(await bytes(src)).join(" ")); const bitmap = await createImageBitmap(src, {imageOrientation: "from-image"}); const canvas = document.createElement("canvas"); canvas.width = bitmap.width; canvas.height = bitmap.height; canvas.getContext("2d").drawImage(bitmap, 0, 0); for (const type of ["image/jpeg", "image/webp", "image/png"]) { const out = await new Promise(resolve => canvas.toBlob(resolve, type, 0.92)); console.log(type, "->", out.type, out.size, listBlocks(await bytes(out)).join(" ")); } })(); 

The input should include EXIF, XMP, an ICC profile and an APP13 segment. The type after the arrow is the type actually returned by the encoder.

In the recorded check on 2026-11-11, the snippet matched the harness byte counts and recorded block lists for this photo in all three builds. WebKit returned image/png for the WebP request, at 8,191,145 bytes, matching the size of its explicit PNG export.

Run records

The main Chromium run used site and harness commit eacd79c, harness version 1.1.0, on 2026-11-10. The Firefox and WebKit run used harness commit 349005d, version 1.2.0, with the same site code on 2026-11-11.

Results are recorded in results-2026-11-10-r5.json, results-2026-11-11-metadata.json and its repeat file. The repeat matched after timing fields were excluded; the main Chromium run was not repeated at this code version.

The repository and raw results are private. The linked project page provides summary tables, not a publicly reproducible copy of the full benchmark.

The 25 CC0 test photos (Wikimedia Commons)
# File on Commons Author as recorded on Commons Licence Pixels Bytes
1 Советский Будильник 12ч34м.jpg Petra Dr CC0 2896×1944 2,186,980
2 Alone in the unspoilt wilderness (Unsplash).jpg David Marcu davidmarcu CC0 3506×2329 3,655,400
3 Brickwall (Unsplash).jpg Namroud Gorguis namroud CC0 2784×1856 5,603,777
4 Coffee at work (Unsplash).jpg Cathryn Lavery cathrynlavery CC0 2400×1600 985,861
5 Friends 1 (Unsplash).jpg Joel Mwakasege joelmwakasege CC0 2592×1933 316,480
6 Green grass texture (Unsplash).jpg Marc Wilnauer wk25 CC0 3232×1824 3,497,050
7 Healthy Cafe Food (Unsplash).jpg Jade Wulfraat jadew CC0 2936×1960 2,812,198
8 Kolkata City at night wide view.jpg XYZ 91973 CC0 8632×864 533,436
9 Landscape-mountains-clouds-trees (24326873785).jpg www.Pixel.la Free Stock Photos CC0 5184×1520 2,864,749
10 Leaf texture in macro (Unsplash).jpg Vadim Gromov motioq CC0 3885×2590 3,557,162
11 Lighthouse at night (Unsplash).jpg Janus Y janus CC0 3888×2592 2,563,831
12 Log pile in close-up (Unsplash).jpg Sven Scheuermeier sveninho CC0 2560×1707 2,616,623
13 Man with tribal face paint (Unsplash).jpg Scott Umstattd scott_umstattd CC0 3464×2309 4,303,508
14 Marmolada, Italy.jpg Marco Bonomo CC0 3904×2604 3,396,302
15 Organic Carrots (Unsplash).jpg Harshal Hirve harshalhirve CC0 4160×2416 2,392,141
16 Pieces of Watermelon (Unsplash).jpg Neha Deshmukh nehadeshmukh CC0 2000×3000 3,070,980
17 Portrait of Prof Mikhail Lemeshko.jpg not recorded CC0 2034×2717 742,583
18 Rangpur city highway at night 1.jpg Cerium4B CC0 2048×1536 450,984
19 Singapore night (Unsplash).jpg Pierpaolo Lanfrancotti goofy1981 CC0 4153×2535 1,122,928
20 Small and medium pumpkins (Unsplash).jpg Corey Blaz blazphoto CC0 4032×3024 2,285,378
21 Smiling Eyes (Unsplash).jpg Luca Campioni white73widow CC0 2136×1536 981,696
22 Star night (Unsplash).jpg Pasi Jormalainen paatse CC0 2488×1558 878,879
23 Strong woman (Unsplash).jpg Scott Webb scottwebb CC0 4190×2782 3,846,153
24 Woman watching the sunset (Unsplash).jpg Lucas Gallone lucasgallone CC0 3264×1836 1,491,446
25 Woman with a pink flower (Unsplash).jpg Camila Damásio miladamasio CC0 3331×2221 4,663,027