Web Crypto Hash Demo — Free SHA-256/384/512 Live Text Hasher

Web Crypto Hash Demo · Forms · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Real crypto.subtle.digest()
Genuine SHA-2/SHA-1 digests, not a lookup or mock.
Correct hex conversion
Byte-by-byte padStart(2,'0') mapping, verified against test vectors.
Four algorithms
SHA-256, SHA-384, SHA-512, and SHA-1 selectable.
Debounced live updates
One digest per pause, not per keystroke.
UTF-8 correct input
TextEncoder converts strings to bytes before hashing.
Secure-context detection
Explains the real crypto.subtle availability requirement.
One-click copy
Copies the current digest with a confirmation state.
Zero dependencies
No hashing library — the browser does the actual work.

About this UI Snippet

Web Crypto Hash Demo — Real SHA-2 Digests Computed On Your Device

Screenshot of the Web Crypto Hash Demo snippet rendered live

This snippet is a genuine cryptographic hash tool, not a lookup table or a fake spinner: every keystroke runs the typed text through the browser's native crypto.subtle.digest(), and the hex string on screen is the actual digest — verifiable against any other SHA-256 implementation.

The real call: crypto.subtle.digest()

Text first goes through new TextEncoder().encode(text), which turns the string into a UTF-8 Uint8Array — hashing always operates on bytes, not JavaScript's UTF-16 string representation. That byte array is passed to window.crypto.subtle.digest(algorithm, data), an async method that returns a promise resolving to an ArrayBuffer containing the raw digest bytes (32 bytes for SHA-256, 48 for SHA-384, 64 for SHA-512, 20 for SHA-1).

Correct ArrayBuffer-to-hex conversion

The result is a raw ArrayBuffer, not a readable string, so bufferToHex() wraps it in a Uint8Array view, maps each byte through byte.toString(16).padStart(2, '0') to get a zero-padded two-character hex pair, and joins with no separator. This is the textbook-correct pattern for the job — the padStart matters because a byte like 5 (0x05) would otherwise render as a single hex digit 5 instead of 05, silently shortening and corrupting the digest string. You can verify the output against any known test vector: hashing "The quick brown fox jumps over the lazy dog" (this demo's default text) with SHA-256 must produce d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592.

Debounced, not throttled

Every keystroke calls scheduleHash(), which clears any pending timer and sets a new 150ms one before actually hashing — so a fast typist only triggers one digest computation after they pause, not one per character. digest() is cheap for short strings, but debouncing keeps the UI from recomputing on every single keypress of a long paste.

The one real caveat: secure contexts only

Unlike the flaky, permission-gated APIs elsewhere in this library, crypto.subtle is broadly supported in every modern browser — but it is only exposed in a *secure context*: an HTTPS origin, or localhost during development. On a plain http:// page, window.crypto.subtle is simply undefined. The code checks for that explicitly and disables the inputs with a message naming the actual requirement, rather than letting the first digest() call throw a confusing TypeError. Pair this with a password generator for a broader "client-side crypto toolkit" page, or a password strength meter.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to walk through exactly why TextEncoder is needed before hashing (rather than passing the raw JavaScript string) and why the ArrayBuffer-to-hex conversion requires padStart(2, '0') on each byte specifically. It's also a good way to sanity-check correctness — ask it to trace the default sample text through SHA-256 by hand or verify the expected digest against a known test vector, and to explain precisely why crypto.subtle exists only in secure contexts and what that means for local development versus production hosting. For extensions, ask it to add HMAC support via crypto.subtle.importKey and sign, add a file-upload mode that hashes a File's bytes instead of typed text, or add a visual "avalanche effect" demo showing how much the output changes for a one-character input change. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a "Web Crypto hash demo" in plain HTML, CSS, and JavaScript using the real browser crypto.subtle.digest() API — no hashing libraries.

Requirements:
- A textarea for input text and a row of buttons to select the hash algorithm (SHA-256, SHA-384, SHA-512, SHA-1), with the currently selected algorithm visually highlighted.
- On input (debounced by roughly 150ms so it doesn't recompute on every single keystroke) and on algorithm change, encode the current text with new TextEncoder().encode(text) and pass the resulting bytes to await window.crypto.subtle.digest(algorithmName, data), which returns a Promise<ArrayBuffer> containing the raw digest.
- CRITICAL: convert the resulting ArrayBuffer to a lowercase hex string using the textbook-correct pattern -- wrap it in a Uint8Array, map each byte through byte.toString(16).padStart(2, '0') to get a zero-padded two-character hex pair, and join the array with no separator. Get this exactly right; padStart is required or single-digit hex bytes will corrupt the output. Verify your implementation would produce d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592 when SHA-256-hashing the text "The quick brown fox jumps over the lazy dog".
- Display the resulting hex digest in a monospace, word-broken output block labeled with the active algorithm, plus a "Copy" button using navigator.clipboard.writeText with a brief confirmation state.
- Handle the rare but real unsupported case: crypto.subtle is only available in a secure context (HTTPS or localhost), so check for its existence (window.crypto && window.crypto.subtle && window.crypto.subtle.digest) before wiring up hashing. If unavailable, disable the inputs and show a status message explaining specifically that a secure context is required -- do not let an unguarded digest() call throw an unhandled error.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSThe default text hashes immediately with SHA-256.
  2. 2
    Type or paste your own textThe digest recomputes live, debounced by 150ms.
  3. 3
    Switch algorithmsChoose SHA-256, SHA-384, SHA-512, or SHA-1.
  4. 4
    Copy the digestClick Copy to grab the current hex string.
  5. 5
    Verify itCompare the output against any standard SHA implementation.
  6. 6
    Served over http://?Inputs disable with an explanation — serve over HTTPS instead.

Real-world uses

Common Use Cases

Dev/security tools
A quick SHA-256 scratchpad alongside a password generator.
File-integrity education
Teach how a checksum changes with any input change.
Config/checksum widgets
Show a settings blob's hash for change detection.
API/webhook debugging
Manually compute a payload digest to compare against a signature.
Onboarding/demo pages
Demonstrate real client-side crypto with no backend.
CS/security teaching tools
Show how tiny input changes cascade the whole digest.

Got questions?

Frequently Asked Questions

It's a real digest. The typed text is UTF-8 encoded with TextEncoder and passed to the browser's native window.crypto.subtle.digest(algorithm, data), which returns the actual cryptographic hash as an ArrayBuffer. You can verify it: hashing the default sample text "The quick brown fox jumps over the lazy dog" with SHA-256 produces d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592, matching any standard SHA-256 implementation.

digest() returns a raw ArrayBuffer, so the code wraps it in a Uint8Array, maps each byte through byte.toString(16).padStart(2, '0'), and joins the results with no separator. The padStart(2, '0') is essential -- without it, a byte value like 5 (hex 0x05) would render as a single character "5" instead of "05", silently truncating and corrupting the digest.

crypto.subtle is only exposed in a secure context: an HTTPS origin or localhost during local development. On a plain http:// page (not localhost), window.crypto.subtle is undefined even in an up-to-date browser, since the Web Crypto API is deliberately restricted to secure contexts. The code checks for this and disables the inputs with an explanation rather than letting the first digest() call throw.

digest() is genuinely cheap for short strings, but hashing on every single keypress during a fast paste or fast typing would still mean many redundant computations whose results are immediately discarded. A 150ms debounce (clearing and resetting a timer on each input event) collapses that into one digest call after the user actually pauses, which is smoother without adding noticeable lag.

Keep the input text and selected algorithm in component state, and in an effect (or watcher) debounce a call to window.crypto.subtle.digest(algorithm, new TextEncoder().encode(text)), converting the resulting ArrayBuffer to hex with the same byte-mapping loop. Because digest() is a promise, guard against a stale response landing after a newer one by tracking a request id or using an AbortController-style flag.