I work on OnePassApps, the team behind app.onepassapps.com.
The Password Generator is one of the apps in the membership. It makes random passwords and passphrases in the browser tab and labels every result with a bit estimate. This post is a measured look at that estimate: what the app shows, what the shipped bundle actually calculates, and where the two agree.
Everything below was measured on the live app at app.onepassapps.com/apps/password-generator on 6 November 2026 and cross-checked against the shipped JavaScript chunk /assets/PasswordGenerator-BziZWL5B.js (29,350 bytes), fetched over plain HTTPS with no session and no cookies.
The pools
Four toggles, and the shipped bundle gives each one an exact character set:
- Lower case - 26 characters, a to z
- Upper case - 26, A to Z
- Digits - 10, 0 to 9
- Symbols - 27,
!@#$%^&*()-_=+[]{};:,.<>?/~
That is 89 characters in total. A fifth switch, "Avoid look-alike characters", removes 0, O, 1, l, I and | from those sets, which takes the pools to 25, 24, 8 and 27, or 84 in total. The app says as much in its own label: "Leaves out 0 O 1 l I and |".
I checked the sets live rather than trusting the source: with only Symbols enabled, ten generated passwords used exactly the 27 characters listed above, and the same sweep on the other three pools matched 26, 26 and 10.
The number describes the settings, not the text
The app states this under the result: "An estimate from how this result was generated (its settings), not from the text itself. It assumes an attacker knows the settings."
The bundle confirms it. The bit count comes from the length and the enabled pools, then gets binned: below 50 is weak, 50 to 79 is fair, 80 to 99 is strong, 100 or more is very strong.
Two settings move it. Turning Symbols off at length 20 took the reading from 129 bits to 119. Turning "Avoid look-alike characters" on at length 20 took it to 127 bits, because the pool drops from 89 characters to 84.
The length sweep, measured
| Length | Bits | Label |
|---|---|---|
| 8 | 50 | Fair |
| 9 | 57 | Fair |
| 10 | 64 | Fair |
| 11 | 70 | Fair |
| 12 | 77 | Fair |
| 13 | 83 | Strong |
| 14 | 90 | Strong |
| 15 | 96 | Strong |
| 16 | 103 | Very strong |
| 17 | 109 | Very strong |
| 18 | 116 | Very strong |
| 19 | 122 | Very strong |
| 20 | 129 | Very strong |
| 128 | 828 | Very strong |
All fourteen readings are reproduced by the formula in the shipped bundle. With "Require each chosen type" enabled, the bundle counts the strings that contain at least one character of every enabled pool - inclusion-exclusion over the pools - and takes the base-2 logarithm of that count. With the toggle off it falls back to length times log2 of the pool size. At length 20 with the default settings both routes land on 129 bits, which is why the toggle looks inert until the password gets short: derived from the same formula, the require-each route returns 50 bits at length 8 where the plain pool formula returns 51.
Passphrases
The second mode draws from a word list instead of a character pool: 3 to 12 words, a separator of hyphen, space, dot, none or a custom separator of up to three characters, optional capitals, and an optional trailing number from 0 to 99.
Measured: 3 words 33 bits, 5 words 56 bits (the default, labelled fair), 8 words 90 bits, 12 words 135 bits. Every reading matches words times log2 of 2,565, the list size the app quotes on screen - 11.32 bits per word. The app's own sentence explains the rest: "Capitals and the separator add no strength." The optional trailing number is the one addition in the bundle's formula: it adds log2 of 100.
Where the randomness comes from
crypto.getRandomValues, and the bundle refuses to run without it: "This browser has no secure random number source, so nothing was generated."
Where a plain modulo would bias the outcome, the bundle takes the largest multiple of the target below 2^32 and rejects anything above it before reducing modulo - rejection sampling, so each character is uniform across its pool.
During the measured run the page made 126 requests. None was a POST, none carried a request body, and none contained the generated password. The only non-asset calls were the shell's own configuration endpoints, /api/runtime and /api/config/feature-flags. The app's own line is "Nothing leaves this tab."
Try it
Standard is free during the current promotional period, no card needed, and the generator needs no sign-in.

