Build a hash generator — MD5, SHA‑1 and SHA‑256 in the browser
A cryptographic hash turns any input into a fixed-length fingerprint. The browser can compute most of them natively — and the one it can't, you can write in about forty lines. Here is the whole tool, front to back, the same way our Hash Generator is built.
What a hash actually is
A hash function maps data of any size to a fixed-size digest — 128 bits for MD5, 160 for SHA‑1, 256 for SHA‑256.1 The same input always produces the same digest, a one-character change scrambles the whole output, and you cannot run it backwards. That makes hashes ideal for verifying downloads, spotting duplicate files, and indexing content — which is all this tool does.
SHA hashes come free with the browser
The Web Crypto API exposes crypto.subtle.digest(), which computes SHA‑1, SHA‑256, SHA‑384 and SHA‑512 natively and asynchronously.2 It takes an algorithm name and an ArrayBuffer of bytes, and resolves to an ArrayBuffer of the digest.
async function sha(algo, bytes) {
const digest = await crypto.subtle.digest(algo, bytes);
return hex(digest); // "SHA-256", "SHA-1", "SHA-512" …
}
function hex(buffer) {
return [...new Uint8Array(buffer)]
.map(b => b.toString(16).padStart(2, "0"))
.join("");
}The digest comes back as raw bytes, so hex() walks the Uint8Array and formats each byte as two hex characters. One caveat: crypto.subtle is only available in a secure context — HTTPS or localhost.3 That is the norm for any modern site, but worth knowing if a hash silently comes back empty on plain http://.
Turning text and files into bytes
Both entry points end at the same place: a Uint8Array. For text, TextEncoder gives you UTF‑8 bytes.4 For a file, FileReader (or the newer Blob.arrayBuffer()) reads the raw contents — and because it reads locally, the file never touches a network.5
// text
const bytes = new TextEncoder().encode(inputEl.value);
// file
const buf = await file.arrayBuffer();
const bytes = new Uint8Array(buf);The catch: MD5 isn't in Web Crypto
Deliberately, the Web Crypto API omits MD5 — it is too broken to be offered as a primitive.6 But people still need it to check legacy checksums, so you supply it yourself. MD5 is defined in RFC 13217 and ports to a compact, dependency-free function over a byte array. The heart of it is 64 rounds of bit-mixing:
function md5(bytes) {
// per RFC 1321: pad to 512-bit blocks, append 64-bit length,
// then run 64 rounds of F/G/H/I over four 32-bit words.
const s = [7,12,17,22, 5,9,14,20, 4,11,16,23, 6,10,15,21 /* ×4 */];
const K = [];
for (let i = 0; i < 64; i++)
K[i] = Math.floor(Math.abs(Math.sin(i + 1)) * 2 ** 32) | 0;
let [a0, b0, c0, d0] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476];
// … process each 64-byte block, updating a0–d0 …
return leHex(a0) + leHex(b0) + leHex(c0) + leHex(d0);
}The one part people get wrong is endianness: MD5 reads and writes its words little-endian, so both the block-loading step and the final hex output run byte-by-byte in reverse. Test against the known vector MD5("abc") = 900150983cd24fb0d6963f7d28e17f72 and you will know instantly if you have it right.7
Wiring the interface
With one bytes payload, you fan out to every algorithm at once and drop the results into the page. Recompute on every keystroke; the digests are microseconds fast for text.
async function recompute(bytes) {
const [md, s1, s256] = await Promise.all([
Promise.resolve(md5(bytes)),
sha("SHA-1", bytes),
sha("SHA-256", bytes),
]);
render({ MD5: md, "SHA-1": s1, "SHA-256": s256 });
}Copy-to-clipboard is a one-liner with navigator.clipboard.writeText(), and an "uppercase" toggle is pure formatting on the already-computed hex.
The part you must not skip
MD5 and SHA‑1 are cryptographically broken. Practical collision attacks against MD5 have existed for years, and SHA‑1 was broken in public in 2017.8 Use them only for non-adversarial checksums and de-duplication — never for signatures.
And no plain hash — not even SHA‑256 — is a way to store passwords. Password storage needs a slow, salted, memory-hard function such as Argon2, scrypt or bcrypt, precisely because fast hashes are trivial to brute-force at scale.9 A hash generator is a fingerprinting tool, not a security boundary. Build it, use it for what it is good at — and reach for the right primitive when the stakes are real.