What a hex editor actually shows you
Every file is a run of bytes. A text editor guesses which encoding those bytes are in and shows you characters; a hex editor does not guess. It shows you the bytes themselves, sixteen to a row, written as two hexadecimal digits each — and beside them, the same bytes read as ASCII so the parts that happen to be text are still readable.
0000002048 65 6C 6C 6F 20 FF 00Hello ..
That is why a hex editor is what you reach for when a file will not open. A PNG that a viewer rejects starts with 89 50 4E 47 when it is intact; a JSON file that a parser refuses often begins with EF BB BF, a byte order mark nothing told you about. Neither is visible in an editor that is busy rendering characters.
Reading the grid
The left column is the offset — how far into the file the row starts — written as eight hexadecimal digits. The middle is sixteen bytes, uppercase, with a wider gap after the eighth so you can find byte eleven without counting to it. The right is the same sixteen bytes as ASCII, where every printable character shows as itself and everything else as a dot.
The two columns are one selection. Clicking a byte in either one moves the caret in both, and dragging across the ASCII column selects exactly the bytes it covers — because there is only one selection underneath, held as byte offsets rather than as characters.
A byte of 00 is dimmed, which is the fastest way to see structure: padding, alignment and empty records show up as pale blocks without your having to read a single number.
Editing a byte, one nibble at a time
Typing in the hex column replaces a byte rather than inserting one, and it takes two keystrokes because a byte is two hexadecimal digits. The first digit is shown with an underscore after it, so you can always see that the byte is half-typed and has not been written yet. The second digit completes it and the caret steps on.
4Dtype AA_type FAF
Press Escape and the half-typed byte is abandoned with nothing written. Press Tab and the caret moves to the ASCII column, where typing a character writes that character's byte directly — which is the quicker way to fix a filename embedded in a header.
Delete writes zeroes over everything selected, in one undoable step. Backspace zeroes the byte behind the caret and steps back onto it. Neither removes a byte, because a file's length is fixed here: this editor overwrites, and every edit is one Ctrl+Z away from being gone.
The data inspector, and why byte order matters
The panel on the left reads the bytes starting at the caret as every type they could be. It updates as the caret moves, and a reading the file has run out of bytes for shows a dash rather than disappearing — so four bytes from the end you can see that an Int64 did not fit. It closes when the grid needs the width — from the cross in its own heading, or from the toolbar — and every reading comes back exactly as it was.
- IntegersSigned and unsigned, at 8, 16, 24, 32 and 64 bits, plus the raw byte in binary and octal. The 64-bit rows are exact — they are read as big integers, not as floating-point approximations of them.
- FloatsHalf, single and double precision. Half precision is what a great deal of machine-learning data and some graphics formats store, and it is the reading that is hardest to work out by hand.
- TextThe byte as ASCII, the first code point as UTF-8, and the code unit — or the surrogate pair — as UTF-16. A malformed or truncated sequence reads as a dash rather than as a replacement character, because a replacement character looks like something that is really in the file.
- GUIDSixteen bytes as a GUID, in whichever of its two layouts the byte order asks for.
Byte order is not decoration. The same four bytes are 67305985 read little-endian and 16909060 read big-endian, and the same sixteen bytes name two entirely different GUIDs depending on whether whoever wrote them followed RFC 4122 or Microsoft's mixed layout. Network protocols are usually big-endian; almost everything written by an x86 or ARM machine is little-endian.
00 11 22 33 …00112233-4455-…or33221100-5544-…
19 readings in all, recomputed the instant the caret or the byte order changes.
Searching by text or by bytes
Search takes either form. Text mode encodes one byte per character, which is what you want for a string embedded in a binary. Hex mode takes the bytes directly, which is what you want for a file signature or a struct field you already know the shape of.
Helloor48 65 6C 6C 6F
A needle is at most 64 bytes, and a search collects at most 1,000 matches before it stops looking and says so — ten million highlighted matches is not an answer anybody asked for. Every match is highlighted in both columns, and Next and Previous wrap around the ends of the file.
The search runs when you press Find rather than as you type. That is deliberate: a scan is linear in the size of the file, and on a file of a few hundred megabytes running one per keystroke would make the box unusable.
The bar sits above the grid rather than over it, which is the whole point of it being a bar: the matches it highlights stay on screen while you step through them. Enter moves to the next match and Shift+Enter to the previous one, the counter between the arrows says which of them the caret is on, and Escape closes the bar and hands the keyboard back to the bytes.
Making room for the bytes
A sixteen-byte row wants width, and the data inspector wants the same width. The two view controls at the end of the toolbar decide which of them gets it: one closes the inspector and gives the grid the whole card, the other takes the editor full screen, where it fills the window rather than the strip of page it normally has. Both are toggles, so the button shows which state you are in rather than what pressing it would do.
Full screen moves the editor into a dialog rather than opening a second one. Escape leaves it and so does the toolbar button, the caret's row is scrolled back into view on the way in and on the way out, and the selection, the undo stack and the search results are all still there — including on a file of a few hundred megabytes, which is never re-read.
Every keyboard shortcut
The grid is fully keyboard-operable. Everything below works while the grid has focus, except the file and dialog shortcuts, which work anywhere on the page.
| Keys | What it does |
|---|---|
Ctrl+O | Open a file |
Ctrl+S | Save over the opened file, or download an edited copy where the browser cannot |
Ctrl+Shift+S | Save to a new file |
Ctrl+F | Open the find bar, or put the caret back in it |
Ctrl+G | Jump to an offset |
Ctrl+Z | Undo the last edit |
Ctrl+Y | Redo the edit that was undone |
Tab | Switch typing between the hex and ASCII columns |
↑ ↓ ← → | Move the caret one byte or one row |
Shift + ↑ ↓ ← → | Grow or shrink the selection from where it was anchored |
Ctrl+A | Select the whole file |
Home / End | Jump to the start or the end of the current row |
Ctrl+Home / Ctrl+End | Jump to the first or last byte of the file |
Page Up / Page Down | Move by as many rows as the grid is showing |
Delete | Write zeroes over the selection |
Backspace | Abandon a half-typed byte, or zero the byte behind the caret |
Opening, saving, and what leaves your machine
Nothing does. The file is read into this tab with the browser's own file APIs and edited in memory; there is no request that carries a byte of it anywhere, which you can confirm with the network panel open.
Where the browser supports the File System Access API — Chrome, Edge and Opera today — a file opened through the Open button comes with a handle, and Save writes back over the original. Everywhere else, and for any file that arrived by drag and drop, Save hands you an edited copy through the download flow instead. The editor says which of the two you are getting above the grid, rather than leaving you to find out from your downloads folder.
Files are accepted up to 512 MB. The ceiling is memory rather than addressing: the bytes are held whole so that reading one stays a single array lookup, and a tab holding much more than that is a tab the browser is about to close. Only the rows on screen are ever built into elements, so scrolling a large file costs the same as scrolling a small one.
When you need one
Identifying a file whose extension is lying: the first few bytes of almost every format are a signature, and reading them is faster than guessing. Checking that a binary patch landed where you meant it to. Finding a hard-coded string in a firmware image. Reading a struct out of a saved game or a packet capture, which is what the inspector's endianness selector exists for.
It is also the fastest way to settle an argument about an encoding. Paste a file in, look at the first three bytes, and a byte order mark either is there or is not — no library, no guessing, and nothing uploaded to find out.
Frequently asked questions
Delete writes zeroes rather than removing bytes, and Backspace zeroes the byte behind the caret. Insertion changes every offset after it, which for most of what a hex editor is used for is a way to break a file rather than to fix one.01 02 03 04 are 67305985 one way and 16909060 the other — same bytes, different number.