OTP Generator
Generate standards-compliant TOTP and HOTP codes from any secret or otpauth:// URI β with a live QR preview and a clock-drift check most generators don't offer.
How to Generate a Code
Features
What's Actually Happening Behind a Six-Digit Code
A one-time code isn't random in the way a rolled die is β it's deterministic, derived entirely from a shared secret and a moving input (time for TOTP, a counter for HOTP) via HMAC, the same keyed-hashing primitive used throughout modern cryptography. Both the app on your phone and the server verifying your login run the identical calculation independently; because they share the same secret and (roughly) the same clock or counter position, they arrive at the same six digits without ever communicating during the login itself. That's the entire security model in one sentence β anyone who has the secret can compute valid codes forever, and anyone who doesn't, can't, no matter how many codes they've already seen.
TOTP vs. HOTP β Same Math, Different Moving Part
RFC 4226 defined HOTP first: a counter that increments by exactly one every time a code is generated and used, keeping both sides in lockstep as long as neither generates a code the other never sees. RFC 6238 adapted the identical HMAC-truncation math for TOTP, but swapped the counter for floor(currentUnixTime / period) β which sidesteps HOTP's synchronization fragility (a phone that generates ten codes nobody uses doesn't desync a time-based system the way it would a counter-based one) at the cost of requiring both sides to agree on the current time within a small tolerance window. This is why virtually every consumer 2FA rollout since roughly 2013 has standardized on TOTP β it tolerates a phone briefly losing network connectivity far better than HOTP tolerates a counter drifting out of sync.
Why the HMAC Runs Through the Browser's Own Crypto Engine
The actual signing step β turning a secret and a counter into a raw HMAC output before it gets truncated down to six or eight digits β runs through crypto.subtle.sign, the Web Crypto API's native implementation, rather than a hand-written HMAC routine in plain JavaScript. That matters for more than just speed: a hand-rolled cryptographic primitive is exactly the kind of code that can look correct on every test vector and still harbor a subtle timing or edge-case bug a native, browser-vendor-maintained implementation doesn't have. The same reasoning applies to the Random Secret button β it draws its bytes from crypto.getRandomValues, the browser's cryptographically secure random number source, not Math.random(), which is explicitly documented as unsuitable for anything security-related.
Base32 encoding and decoding, on the other hand, is genuinely just data formatting β no secrecy or randomness involved β so that part runs as plain, auditable JavaScript directly in this tool rather than needing any special API. The distinction is deliberate: use the platform's trusted crypto primitives for anything where correctness actually matters to security, and keep everything else simple and inspectable.
Diagnosing "The Code Doesn't Match" the Right Way
The instinctive reaction to a mismatched code is to assume the secret was copied wrong β and sometimes that's exactly it β but clock drift causes this far more often than people expect, especially on virtual machines, containers, and devices that have been offline for a while. Because TOTP's counter is just time divided into windows, a device even 30-40 seconds out of sync with real time will compute the adjacent window's code instead of the current one. That's precisely why this tool always shows Previous, Current, and Next side by side rather than a single value: if what you're comparing against lines up with "Previous" or "Next," the fix is checking system time, not re-entering the secret. If none of the three match, then the secret, algorithm, digit count, or period genuinely differs between the two sides β worth rechecking each one individually rather than guessing.
Working With the otpauth:// URI and QR Code
Every 2FA setup screen you've ever scanned encodes an otpauth:// URI into its QR code β the same structured format this tool both reads and writes. Pasting one in configures the secret, issuer, account label, algorithm, digit count, and period or counter in a single step, which matters most when you're reconstructing a setup from a backup you saved as plain text rather than a screenshot. Going the other direction, the QR code rendered here from your current settings is genuinely scannable by any real authenticator app β a fast way to confirm a custom 2FA implementation matches the standard before shipping it, without needing to hand-type a long Base32 secret into a phone keyboard.
Where This Fits Into Real Development and Recovery Work
- Testing a 2FA implementation you're building: generate codes from the same secret your backend uses and confirm both sides agree before it ever reaches a real user.
- Debugging a support ticket about failed logins: reproduce the exact code a user's app should be showing, and check it against the adjacent-window view for drift.
- Recovering account access from a saved backup secret: paste it in and get working codes immediately, without waiting on a support flow.
- Verifying a QR code before scanning it into your real authenticator app: decode and inspect the underlying secret and settings first if you're at all unsure of its source.
What This Tool Isn't For
It doesn't store secrets between visits, sync across devices, or manage multiple accounts the way a dedicated authenticator app or a password manager's built-in TOTP vault does β every value here lives only in the current page session and disappears the moment you navigate away, by the same design principle used across every security tool on this site. If you need a permanent, secure home for 2FA secrets, use a real authenticator app or your password manager's TOTP feature; this tool is for testing, debugging, and occasional recovery, not day-to-day account protection. For generating the strong, unrelated password each account should still have alongside 2FA, see Password Generator.