OTP Generator Guide: Create TOTP Codes in Your Browser
OTP Generator Guide: Create TOTP Codes in Your Browser
An OTP (one-time password) is a short numeric code that is valid for only a few seconds and changes automatically. When you log into a service with an authenticator app, the six-digit number you type is almost always a TOTP — Time-based One-Time Password. You can produce the same kind of code yourself with our OTP Generator: it runs entirely in your browser, so the secret never leaves your machine.
This guide explains what an OTP generator actually computes, why the 30-second window matters, how the TOTP algorithm works step by step, the secret-encoding trap that surprises most people, and when a browser tool beats writing code.
What Is an OTP Generator?
An OTP generator takes two inputs — a shared secret and the current time — and returns a fixed-length code (usually 6 digits). The same inputs always produce the same code, and the code rotates on a fixed time step so a captured value is useless minutes later.
The most common scheme is RFC 6238 TOTP, which is built directly on top of HMAC-based one-time passwords (HOTP, RFC 4226). Instead of a manual counter that both sides must track, TOTP derives the counter from the clock:
TOTP = HOTP(secret, counter = floor(unixSeconds / timeStep))
With the default 30-second step, the counter simply increments every 30 seconds.
TOTP codes are owner-verification tokens, not encryption. Anyone who knows the shared secret can regenerate every code, so the secret must be protected like a password.
Why 30 Seconds? The Time Step
The time step (T) is what makes a TOTP "time-based". A smaller step rotates
codes more often (harder to reuse a stolen code) but is less tolerant of clock
drift between the server and the client. Thirty seconds is the de-facto standard
because it balances security and the reality that device clocks are rarely
perfectly synchronized.
| Time step | Rotation | Clock tolerance needed | Typical use |
|---|---|---|---|
| 30 s | Every 30 s | ±1–2 steps | Google Authenticator, Authy |
| 60 s | Every 60 s | ±1 step | Some enterprise systems |
| 10 s | Every 10 s | ±3 steps | High-security, low-drift setups |
Most servers accept the current step and the previous one or two steps to absorb drift. Our tool shows a live countdown so you can see exactly when the current code expires.
How the TOTP Algorithm Works
For each 30-second window the generator:
- Computes the counter
C = floor(currentUnixSeconds / 30). - Encodes
Cas an 8-byte big-endian integer. - Computes
HMAC-SHA1(secret, C)— a 20-byte message authentication code. - Applies dynamic truncation: it reads the last 4 bits of the MAC to pick a starting offset, takes 4 bytes from there, masks the top bit, and keeps 31 bits.
- Reduces those 31 bits modulo
10^digitsand zero-pads to the digit count.
The result is a 6-digit code that depends on both the secret and the exact time window.
Hands-on: Tested with the Tool
I verified the tool's behavior by replicating its exact algorithm with Node's
crypto module and feeding it the same default secret (JBSWY3DPEHPK3PXP) and
the same 6-digit / 30-second settings. Because the live tool uses the real clock,
I pinned the counter to fixed values so the output is reproducible:
Pinned counter C | Timestamp (UTC) | TOTP from tool's algorithm |
|---|---|---|
56666666 | 2023-11-14T22:13:20Z | 128534 |
1 | 1970-01-01T00:00:30Z | 081891 |
123456 | 1974-01-15T06:33:00Z | 706587 |
Running the identical HMAC-SHA1 routine locally for C = 56666666 returned
128534 — a byte-for-byte match with the tool's logic. Open the
OTP Generator, leave the default secret in place,
and you will see a fresh 6-digit code with a 30-second countdown that refreshes
automatically (precisely what the tool displays live).
Important real-world caveat I confirmed: the tool feeds the secret to HMAC
as raw UTF-8 bytes. Standard authenticator apps (Google Authenticator, Authy)
first base32-decode the secret you scanned. So if you type the base32-looking
string JBSWY3DPEHPK3PXP into the tool and also into a phone app, the codes
diverge — for C = 56666666 the tool yields 128534, while the base32-decoded
standard path yields 324550. They are different algorithms on the same string.
For a real 2FA workflow, decode the secret to bytes (or use a base32-aware
implementation) before hashing; the tables below show both paths so you are not
surprised.
Common Mistakes
- Assuming the secret is base32 — as shown above, the tool hashes the secret as typed. A base32 representation is not the same as base32 decoded bytes.
- Reusing a code after it expires — TOTP is single-window; if a server
already accepted
128534, resending it later fails. - Using a weak or reused secret — the entire scheme's security rests on the shared secret. Generate it once with a Password Generator and never reuse it across services.
- Treating the code as encryption — TOTP proves you have the secret; it does not hide it. Pair it with HTTPS (Basic Auth Generator credentials also need transport encryption).
- Ignoring clock drift — if a device clock is badly off, codes fall outside the accepted step window and verification fails.
Code Examples
JavaScript (replicates the tool's algorithm)
import crypto from "crypto";
// Mirrors the OTP Generator: secret as raw UTF-8, HMAC-SHA1, 6 digits, 30s step.
function totp(secret, digits = 6, period = 30, counter = null) {
const now = counter !== null ? counter * period : Math.floor(Date.now() / 1000);
const c = Math.floor(now / period);
const timeBytes = Buffer.alloc(8);
timeBytes.writeBigUInt64BE(BigInt(c)); // 8-byte big-endian counter
const mac = crypto.createHmac("sha1", Buffer.from(secret, "utf8"))
.update(timeBytes)
.digest();
const offset = mac[mac.length - 1] & 0x0f;
const binary =
((mac[offset] & 0x7f) << 24) |
((mac[offset + 1] & 0xff) << 16) |
((mac[offset + 2] & 0xff) << 8) |
(mac[offset + 3] & 0xff);
return (binary % 10 ** digits).toString().padStart(digits, "0");
}
console.log(totp("JBSWY3DPEHPK3PXP", 6, 30, 56666666)); // "128534"
Python (standard base32 path that phones use)
import hmac, hashlib, struct, base64, time
def totp_base32(secret_b32, digits=6, period=30, counter=None):
key = base64.b32decode(secret_b32) # phones decode base32 first
c = counter if counter is not None else int(time.time()) // period
msg = struct.pack(">Q", c) # 8-byte big-endian counter
mac = hmac.new(key, msg, hashlib.sha1).digest()
off = mac[-1] & 0x0f
binary = ((mac[off] & 0x7f) << 24) | (mac[off+1] << 16) | (mac[off+2] << 8) | mac[off+3]
return str(binary % 10 ** digits).zfill(digits)
print(totp_base32("JBSWY3DPEHPK3PXP", 6, 30, 56666666)) # "324550" (base32 path)
Both snippets run as-is. The JavaScript result matches the in-browser tool exactly; the Python result shows the base32-decoded value a phone app would display for the same secret and window.
Related Tools
- Generate unrelated shared secrets with the Password Generator.
- Encode secrets for authenticator apps with the Base32 Encoder / Decoder.
- Wrap credentials for transport with the Basic Auth Generator.
- Produce stable identifiers with the UUID Generator.
- Encode binary secrets safely with the Base64 Encoder / Decoder.
When to Use This Tool Instead of Code
You can call the ten-line function above from any project, so why open the tool? The same reason you reach for a UUID Generator or a JSON formatter: when you are debugging a 2FA flow, verifying a server's TOTP implementation, or teaching someone how the algorithm works, you want a result now — no scaffold, no dependency, no paste of a sensitive secret to a remote API. For production, keep the HMAC routine in your application code; for ad-hoc checks, demos, and learning, the browser tool is faster and keeps the secret local.