npm package · MIT · v2 · zero deps

passglyph

Hieroglyphs instead of dots. Matching passwords, matching glyphs.

passglyph v2 replaces the masking dots inside your password fields with Egyptian hieroglyphs. If the confirmation matches, both fields show the same sequence; a typo changes every glyph from the first wrong character on. Each glyph comes from a keyed hash of the whole prefix with a fresh per-page key, so nothing about the characters is revealed, and repeated letters don't repeat glyphs. The input stays type="password".

npm i passglyph
The 64-glyph alphabet (U+13000–U+1342F, drawn from a bundled sprite). The default uses the first 32, so 5 bits per position.

Live demo

Mode
Alphabet32 hieroglyphs
A typo slips past one position1 in 32
…past every later position (10 chars)1 in 1015
Leak to a screenshot (no key)0 bits
If the key were stolen too5 bits per position

With 5 bits a single mistyped position is caught 31 times out of 32, and the following positions almost always differ as well. The glyphs reveal nothing without the per-page key, which is non-extractable and never leaves memory.

Threat model, briefly

  • No character-to-glyph mapping. Glyph i = HMAC-SHA-256(key, password[0..i]) truncated to 4–6 bits. Identical characters don't give identical glyphs, and a mismatch shows from the first wrong position on.
  • Fresh 128-bit key per form from crypto.getRandomValues, imported as a non-extractable WebCrypto key. The raw bytes are wiped, so a screenshot or recording is meaningless in any other session.
  • Honest leak: a recording gives about b bits per position under that session's key. That's useless without the key, but with it, prefix hashing lets an attacker recover the password one character at a time (about 95 guesses per position). The key never leaves memory, and an attacker with memory access can read the field anyway.
  • Typo position is visible: the sequences diverge exactly where you mistyped. That's the point, and it's documented.
  • No extra length or timing leak: one glyph per character, exactly like the dots, redrawn in one frame per input with no animation.
  • Canvas only. No code points, glyph names or derived bits in the DOM. The snapshot is identical for any two passwords. Overlays are aria-hidden, and a live region says "Passwords match".
  • Real input untouched: it stays type="password", so autofill, password managers, mobile keyboards and screen readers keep working. It falls back to dots if anything is unsupported.

Not a strength meter, and no help against keyloggers or malicious scripts on the page. Full write-up: SECURITY-DESIGN.md · launch post.

Usage

// Vanilla (inline hieroglyphs by default)

import { attach } from 'passglyph';
const detach = attach(pw, confirm);

// v1 badge mode

attach(pw, confirm, { mode: 'badge' });

<!-- Web component -->

<pass-glyph>
  <input type="password"> <input type="password">
</pass-glyph>

// React / Vue

usePassGlyph(pwRef, confirmRef, { bits: 5 });

Support this project

Support open-source security tools

I build free, open-source tools that help developers ship safer websites. Every coffee helps keep them maintained, documented and free for everyone.

Buy me a coffee (opens Stripe in a new tab)

Secure checkout by Stripe

Portrait of Louis Wright

About the author

Louis Wright

Cyber security researcher and software engineer based in the UK.

More tools by Louis Wright

60 tools

Sentinels security scanners & CI gates · 20