Hex File Compare
Diff two binary files byte by byte. Side-by-side hex dump with offsets, ASCII gutter, differences-only filter. 100% browser-side. Verify patches, firmware, downloads.
About Hex File Compare
Hex File Compare is a byte-level diff for binary files. It reads up to 64 KB from the head of each file and renders them side by side in the canonical hex-dump format pioneered by Unix's `od -x` and HP's HxD: a left-hand offset column in hexadecimal, then 16 bytes per row as two-character hex pairs, then an ASCII gutter where printable bytes (0x20–0x7E) appear as their character and the rest as dots. Rows where the two files differ are highlighted; with 'Differences only' enabled, identical rows collapse so a 64 KB scan reduces to the handful of bytes that matter. The most common professional uses are: verifying a binary patch landed cleanly (compare before/after, count diffs, check offsets match the patch manifest), reverse-engineering a firmware update (diff two versions to spot the bootloader or string table changes), forensic file recovery (compare a suspected corrupted copy against a known-good one to find the corruption boundary), and integrity validation of downloads that report a mismatched checksum but where you want to see *where* the bytes diverge instead of just knowing they do. Comparison runs entirely in your browser using the FileReader API and a typed-array DataView walker — your files never leave the device, even for analytics.
How much of the files are compared and why is there a 64 KB cap?
Up to 64 KB (65 536 bytes) from the start of each file. The cap is a deliberate UX choice rather than a technical limit: rendering more than ~4 000 hex rows in a DOM table starts to feel sluggish on mobile, and most real binary forensics happens in the first few KB anyway — file headers, magic numbers, signatures, certificate blobs, ELF/PE/Mach-O metadata, and patch entry points all live near the beginning. For deep tail-of-file diffs (think rolling logs or appended encrypted payloads), use a desktop tool like HxD, 010 Editor, GHex (Linux), or `cmp -l file_a file_b` on Unix which streams the entire file and reports every byte mismatch with octal values.
How does this compare to `diff`, `cmp`, `git diff` and `vbindiff`?
`diff` is line-oriented and assumes UTF-8 or ASCII text; it gives meaningless output on binaries. `cmp -l a b` is byte-oriented but produces a flat list (offset, byte_a, byte_b in octal) with no context — accurate but hard to read. `git diff --binary` only encodes patch deltas, it does not show them readably. `vbindiff` (the Visual Binary Diff utility) is the closest analogue to this tool and is what we modelled it on: two-panel hex view with synchronised scrolling, ASCII gutter, and highlighted mismatches. The difference is that vbindiff runs in a terminal; this tool runs in your browser, supports drag-drop, and renders without any installation. For massive files or scripted CI checks, prefer `cmp -l` piped to `wc -l` for a quick mismatch count or `radiff2 -x` (radare2's hex differ) for structured output.
Why are some bytes in the ASCII column shown as dots even though they look like they should be letters?
The ASCII gutter follows the strict printable range 0x20–0x7E (space through tilde), which is the convention from the original POSIX `od -c` and BSD `hexdump`. Anything outside that range gets replaced with a single dot to keep each row exactly 16 characters wide for column alignment. This means: tab (0x09), newline (0x0A), null (0x00), all bytes 0x80–0xFF (extended ASCII and multibyte UTF-8 lead/continuation bytes), and DEL (0x7F) all render as dots. If you need to see UTF-8 text encoded in the binary (filenames in a ZIP archive, strings in a Mach-O binary, JSON inside a sandwich-encoded blob), copy the offset of interest and open the file in a UTF-8-aware viewer like 010 Editor or use `strings file | grep` for a quick text dump.
What are the most common binary file signatures (magic numbers) I should look for?
The first few bytes of nearly every file format declare what it is — knowing these turns hex-diffing into a fast format-aware activity. Key magic numbers (bytes 0x00 onward): PNG 89 50 4E 47 0D 0A 1A 0A, JPEG FF D8 FF (then E0/E1/E2/E3/DB for JFIF/EXIF/etc.), GIF 47 49 46 38 ("GIF8"), PDF 25 50 44 46 ("%PDF"), ZIP/JAR/DOCX/XLSX 50 4B 03 04 ("PK\03\04"), RAR4 52 61 72 21 1A 07 00, RAR5 52 61 72 21 1A 07 01 00, 7Z 37 7A BC AF 27 1C, GZIP 1F 8B, BZIP2 42 5A 68 ("BZh"), ELF (Linux executables) 7F 45 4C 46 ("\x7FELF"), Windows PE/EXE 4D 5A ("MZ") with PE header pointer at offset 0x3C, Mach-O (macOS) FE ED FA CE/CF or CE/CF FA ED FE, WAV 52 49 46 46 ("RIFF") then "WAVE", MP4 starts with size bytes then 66 74 79 70 ("ftyp") at offset 4, SQLite 53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00. If your hex view starts with something else, the file is probably encrypted, compressed, or corrupted.

How do I spot a binary patch (DLL/EXE update, firmware delta) in the hex diff?
Binary patches usually cluster their changes in a few small regions rather than spread evenly. With 'Differences only' on, look at the offset spacing of the diff rows: a software patch (security fix, bug fix) typically touches one to three short ranges of 4–64 bytes in the .text or .rdata sections (Windows PE) or .text and .rodata segments (ELF), corresponding to specific instruction patches or constant updates. A firmware update covers wider ranges, often including a checksum or signature at the tail. A version-string bump shows up as a contiguous ASCII change (e.g., '1.0.4' to '1.0.5' at a stable offset). If the diff is uniform — every row shows changes — the two files are unrelated or one was encrypted with a different key. If the diff starts identical then becomes uniform from a certain offset onward, you have likely caught a download corruption boundary or a truncation.
What is the difference between big-endian and little-endian byte order in a hex dump?
Endianness is the order in which multi-byte values (16-bit, 32-bit, 64-bit integers) are stored. Little-endian (Intel x86, AMD64, ARM in default mode) stores the least-significant byte first: the 32-bit value 0x12345678 appears in a hex dump as 78 56 34 12. Big-endian (network byte order per RFC 1700, classic PowerPC, SPARC, m68k, most file-format headers like PNG and JPEG, and IP/TCP headers on the wire) stores most-significant byte first: same value appears as 12 34 56 78. When comparing two binaries built for different architectures, what looks like a giant diff may just be an endianness flip. Quick check: most file headers (PNG, ZIP, ELF, PDF) embed their size or signature fields in a known endianness, so if your hex starts with familiar magic numbers in the right order you have the correct interpretation.
Why are my files declared identical when they should differ (or vice versa)?
Three common gotchas. First, BOM or line endings — text files saved as CRLF (Windows) versus LF (Unix) differ at every newline byte; the tool will faithfully report many small diffs in places where the content is logically identical. Second, embedded timestamps — many file formats (ZIP, DOCX, PDF, executables built with debug info, PNG with creation time chunk) embed creation or modification dates inside the binary, so two files compiled from identical source minutes apart will diff at those offsets. Third, compression non-determinism — gzip, ZIP, and PDF can produce different bytes for the same input depending on compression level, dictionary state, or library version; to compare logical content, decompress both first and diff the contents. To rule out trivial diffs, use `dos2unix file` for line endings, `srzip --no-time` for timestamps, and decompress before comparing.
Are my files uploaded to a server or saved anywhere?
No. The diff runs entirely in your browser. Each file is read via FileReader.readAsArrayBuffer into a Uint8Array, walked byte-by-byte in a tight loop on the main thread (with a yielding microtask every 4 KB to keep the UI responsive), and the resulting hex rows are rendered as plain DOM. No file content, file name, offsets, or diff statistics are sent to our server, logged in analytics, or persisted in localStorage. Open the browser DevTools Network tab before clicking Compare — you will see zero new requests during the comparison and zero requests carrying the file content during render. Closing the tab purges every buffer.
