The pipeline
Read the raw bytes
The file is read into an ArrayBuffer in your browser. No decoding happens at this point — we are looking at the container structure, not the image.
Walk the structure
PNG files are a signature followed by length-prefixed chunks. JPEG files are a chain of marker segments. Both are walked in order, validating lengths so malformed input stops safely rather than reading out of bounds.
Classify and drop
Each chunk or segment is classified as image data or metadata. Metadata is skipped; everything else is copied through unchanged.
Reassemble
The kept pieces are concatenated into a new buffer and handed back as a Blob. Since the compressed pixel data was never touched, the output is pixel-identical.
PNG: chunk by chunk
A PNG is an 8-byte signature followed by a series of chunks, each carrying a 4-byte length, a 4-byte type, the data, and a CRC. Walking that list makes it trivial to keep the image chunks and drop the metadata ones.
| Chunk | What it holds | Verdict |
|---|---|---|
IHDR | Image header — dimensions, bit depth, colour type | Kept |
IDAT | The actual compressed pixel data | Kept |
IEND | End-of-file marker | Kept |
iCCP | ICC colour profile | Kept — removing it shifts colour |
tEXt / iTXt / zTXt | Text chunks; frequently carry prompts and job IDs | Removed |
eXIf | EXIF record embedded in PNG | Removed |
caBX | JUMBF box holding a C2PA manifest | Removed |
tIME | Last-modification timestamp | Removed |
JPEG: segment by segment
A JPEG is a chain of marker segments, each starting with 0xFF and a marker byte. The APP markers hold metadata — but APP2 is shared between ICC colour profiles and C2PA manifests, so that one needs its payload inspected rather than being dropped wholesale.
| Segment | What it holds | Verdict |
|---|---|---|
SOI (FFD8) | Start of image | Kept |
APP0 (FFE0) | JFIF header — required for a valid baseline JPEG | Kept |
APP1 (FFE1) | EXIF records and XMP packets | Removed |
APP2 (FFE2) | ICC profile OR C2PA manifest — same marker | Depends on payload |
APP13 (FFED) | IPTC / Photoshop resource blocks | Removed |
APP11 (FFEB) | JUMBF / C2PA manifest store | Removed |
SOS (FFDA) | Start of scan — entropy-coded image data follows | Kept |
Technical questions
Why is byte-level parsing better than re-encoding?
Re-encoding decompresses your image and compresses it again, which loses quality on every pass and usually discards the colour profile too. Byte-level parsing rewrites only the container, so the compressed pixel data is copied through untouched and the output is bit-identical to the input.
How do you tell an ICC profile from a C2PA manifest in JPEG?
Both can live in the APP2 marker. We read the payload identifier at the start of the segment: an ICC profile begins with the ASCII string "ICC_PROFILE", so segments matching that are preserved and everything else in that marker is removed. Without this check, scrubbing would silently destroy colour profiles.
What happens with a corrupted or unusual file?
Both walkers validate declared lengths against the actual buffer size and stop if a chunk claims to extend past the end. If the structure cannot be parsed confidently the original bytes are returned unchanged, because silently corrupting someone's file is far worse than not scrubbing it.
Why can you not do this for video?
MP4 and WebM store metadata in atoms and elements interleaved with media data, and any visible watermark removal requires decoding and re-encoding every frame. Browsers cannot do that at usable speed, so video support is limited to signature inspection and single-frame export.
See it on your own file
Scan a PNG or JPEG and the tool lists every segment it found before removing anything.
Open the scrubber