How I built a browser-based binary editor
What is a binary editor?
A binary editor is a tool that allows you to edit a file directly in hexadecimal or ASCII notation. While a standard text editor works with “strings” as its editing unit, a binary editor gives you access to the raw byte sequence that makes up a file so you can change any bit or byte at an arbitrary offset.
Binary editors are indispensable for software analysis, data recovery, and protocol investigation. GUI applications such as HxD or Binary Ninja are widely known. My goal this time, however, was to build a single-file binary editor that works entirely in the browser.
Implementation approach
- Fully client-side (no network transmission)
- Use the
FileReader
API to load local files - Render hexadecimal (Hex) on the left and ASCII on the right
- Enter edit mode with a click and overwrite values in place
- Highlight edited cells in red
- Toggle between hex-edit and ASCII-edit modes
- Allow users to download the edited result as a file again
Roadblocks I hit and how I fixed them
1. Misaligned edit highlights
In the initial build, the yellow outline that marks the active edit cell was slightly off-center and looked awkward. CSS line-height
and the layout created by flex
were the culprits: the baseline for the glyph and the baseline for the outline did not line up. Even though I relied on a monospace font, browser rendering pipelines can place the glyph baseline differently for each font, so the outline ended up drifting up or down.
Forcing vertical-align: middle
and turning the cell into an inline-block
so the heights match eliminated the problem.
.hex-cell.editing {
outline: 2px solid yellow;
vertical-align: middle;
}
2. ESC key characters sneaking into cells
Early on, pressing ESC while a cell was active would type characters like “EE” into the cell.
The reason was that I was not intercepting the keydown
event, so the browser handed the key code directly to the edit routine. My input logic only expected 0–9 and A–F, but the Escape
key has a key code as well (keyCode = 27
in legacy code, key = "Escape"
in modern APIs). Without a guard, the handler interpreted that input as the character "E"
, resulting in "EE"
being written.
The fix was straightforward: catch Escape
in the keydown
handler, cancel the edit, and prevent the default processing.
document.addEventListener("keydown", e => {
if (e.key === "Escape") {
cancelEdit();
e.preventDefault(); // keep the key from flowing into the generic input path
}
});
Now ESC only cancels editing and never writes characters.
3. Red highlights persisting after reload
After editing one file, opening a different file left the red highlights behind.
The root cause was that the array and state objects that track modified cells were never reset when a new file was loaded. Clearing the viewport alone was not enough because the “modified” flag survived and made the new file look edited even though it was untouched.
Calling clearModifiedState()
right after the file load wipes the flags and restores the neutral styling.
4. Toggling between HEX and ASCII modes
My first iteration let users edit HEX and ASCII simultaneously. That made it hard to tell which input had priority, and editing the same byte through two routes caused conflicts. Updating ASCII immediately overwrote the HEX view, but in-flight input created transient inconsistencies on screen.
To avoid the confusion, I switched to an explicit select box for mode switching and made HEX edits the default. Users now always know they are entering HEX, and the synchronization logic became much simpler.
A look at the core code
The edit routine ended up looking like this:
function applyEdit(offset, newValue) {
if (mode === "hex") {
buffer[offset] = parseInt(newValue, 16);
} else {
buffer[offset] = newValue.charCodeAt(0);
}
markModified(offset);
render();
}
markModified(offset)
adds the red highlight. It is a straightforward check that sets a CSS class when the value differs from the original.
Takeaways
The browser-based binary editor now runs offline and keeps all processing on the client, making it a safe entirely client-side tool.
- Quick binary fixes when you do not want to install anything
- Investigations in locked-down environments where you cannot add native apps
- Educational settings where you want to build intuition for raw byte sequences
These are the kinds of scenarios where the editor shines.