Skip to content

About

Privacy-first, client-side EXIF metadata viewer and lossless scrubber built with JavaScript ArrayBuffers.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 

Repository files navigation

ScanMetadata: Zero-Upload In-Browser EXIF Parser & Lossless Scrubber

License: MIT Privacy First Live Demo

ScanMetadata is an open-source, privacy-first web utility designed to read, analyze, and losslessly strip EXIF metadata from photos directly inside the browser.

Traditional metadata removers require users to upload confidential images to a remote backend server, introducing severe privacy and data retention risks. The typical client-side workaround—rendering photos onto an HTML5 <canvas>—destroys original image quality through forced lossy re-compression.

ScanMetadata solves this problem by executing raw binary slicing using JavaScript's native ArrayBuffer and DataView APIs. No server uploads, zero quality degradation, and complete client-side sandboxing.

Try the production build online: https://scanmetadata.com/


Key Features

  • 100% Client-Side Execution: File inspection and data removal happen entirely within browser memory. No images, coordinates, or device logs are transmitted to any server.
  • Lossless Binary Stripping: Unlike canvas-based solutions, ScanMetadata bypasses re-encoding. Quantization tables, color spaces, and DCT coefficients remain untouched—only metadata segments are removed.
  • Granular EXIF Extraction: Inspects deep metadata structures, including GPS location data (latitude, longitude, altitude), camera make/model, shutter speed, ISO, aperture, focal length, lens serials, and original capture timestamps.
  • Native Little & Big Endian Support: Accurately decodes both Intel (II / 0x4949) and Motorola (MM / 0x4D4D) TIFF byte alignment headers without byte misinterpretation.
  • Zero Dependencies: Pure vanilla JavaScript implementations for binary reading and marker parsing.

Technical Architecture & How It Works

JPEG files are structured into sequential binary markers, each prefixed with a two-byte delimiter 0xFF followed by a marker byte.

[SOI: 0xFFD8] ──> [APP1 / EXIF: 0xFFE1] ──> [DQT / SOF / DHT] ──> [SOS: 0xFFDA (Pixels)] ──> [EOI: 0xFFD9]
                        │
                        └── Sliced out losslessly in memory

1. Header Marker Detection

The parser reads the first 128KB of the file via the HTML5 File API and checks for the mandatory JPEG Start of Image (SOI) marker 0xFFD8.

2. Segment Traversal

The engine traverses sequential markers (APP0 0xFFE0, APP1 0xFFE1, DQT 0xFFDB, etc.) by reading the 16-bit Big-Endian length descriptor immediately following each marker until reaching Start of Scan (SOS 0xFFDA).

3. Lossless Buffer Slicing

When scrubbing metadata, rather than decoding pixel matrices, ScanMetadata creates a new Blob composed of discrete ArrayBuffer slices that omit the 0xFFE1 (APP1) payload entirely. The pixel data stream remains bit-for-bit identical to the original capture.


Quickstart & Code Example

To strip EXIF markers from an image buffer losslessly without canvas re-encoding:

export async function sanitizeJpeg(file) {
  const buffer = await file.arrayBuffer();
  const view = new DataView(buffer);

  // Validate SOI marker (0xFFD8)
  if (view.getUint16(0, false) !== 0xffd8) {
    return file; // Unsupported format, preserve original
  }

  const cleanChunks = [buffer.slice(0, 2)]; // Keep SOI
  let offset = 2;
  let cursor = 2;

  while (offset < buffer.byteLength - 4) {
    const marker = view.getUint16(offset, false);

    // Stop traversal at Start of Scan (raw pixel stream)
    if (marker === 0xffda || marker === 0xffd9) break;

    const segmentLength = view.getUint16(offset + 2, false);
    const totalBytes = 2 + segmentLength;

    // Check for APP1 EXIF segment (0xFFE1)
    if (marker === 0xffe1) {
      if (offset > cursor) {
        cleanChunks.push(buffer.slice(cursor, offset));
      }
      cursor = offset + totalBytes; // Skip over the EXIF block
    }
    offset += totalBytes;
  }

  if (cursor < buffer.byteLength) {
    cleanChunks.push(buffer.slice(cursor));
  }

  return new Blob(cleanChunks, { type: file.type || "image/jpeg" });
}

Supported Formats

Format Parsing Support Lossless Stripping
JPEG / JPG Full EXIF, GPS, TIFF, JFIF Supported (ArrayBuffer Slicing)
PNG Textual chunks (tEXt, zTXt, iTXt) Planned
WebP EXIF / XMP chunk detection Planned

Privacy & Security Policy

ScanMetadata does not operate analytics trackers, telemetry collectors, or remote storage databases. Every computation is isolated within the user's browser sandbox.

Production Deployment: ScanMetadata Web App

License

This project is licensed under the MIT License.

About

Privacy-first, client-side EXIF metadata viewer and lossless scrubber built with JavaScript ArrayBuffers.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors