Random string generator with custom characters

Paste any alphabet. It is parsed by grapheme, deduplicated before sampling, and the entropy shown is the entropy you get.

G
More options
up to 10,000

Constraints

Your alphabet is parsed as symbols, not as UTF-16 units

In JavaScript, the length of a key emoji is 2, because the string is stored as UTF-16 and the character occupies a surrogate pair. A generator that indexes the alphabet with a plain subscript will therefore pick half a surrogate pair roughly half the time, and emit a lone surrogate that is not a valid character in any encoding. Spreading the string into code points fixes that case.

Code points are still not enough. An accented letter written as a base letter plus a combining acute accent is two code points and one visible character. Splitting by code point puts the accent into the alphabet as an independent symbol, so the output contains stray accents attached to whatever preceded them. The tool segments by grapheme cluster, which is the unit a reader would call a character, and shows you the parsed symbol count so you can check that it agrees with what you pasted.

This is a quality claim, not an exclusivity claim. Of the fifteen generators audited, two handle graphemes correctly: onlinestringtools uses a grapheme splitter, and gigacalculator sidesteps the problem by taking a whitespace-delimited list of tokens. One is code-point safe but not grapheme safe. Two split raw UTF-16 units. A naive implementation here would be worse than two incumbents, which is why the parsing is spelled out rather than assumed.

Duplicates are removed before sampling, and the entropy follows

Paste the alphabet aab and there are two possible outcomes. Either you sample from the raw string, in which case a comes up two times in three and the Shannon entropy of each draw is 0.9183 bits, or you deduplicate first and sample from ab, in which case each draw is exactly 1.000 bits. Both are defensible. What is not defensible is doing one and reporting the other.

That combination is live on a page-one competitor. randomkeygen indexes the raw custom character set when picking characters, but computes its entropy display over a deduplicated set, so aab is sampled at 0.9183 bits per character and reported as 1.000. Their sampler is otherwise textbook-correct, with real 32-bit rejection sampling and a hard failure rather than a fallback when Web Crypto is missing. The defect is in the readout, not the randomness, and it is worth naming precisely because their implementation is good.

This tool deduplicates first, then samples from and reports on the same set. If you paste a set with repeats, the symbol count on screen tells you how many distinct symbols survived, so the entropy figure and the draw always describe the same alphabet.

Repeating a symbol to weight it is a legitimate technique if you actually want a non-uniform distribution. It is not compatible with the length times log2(N) entropy formula, which assumes every symbol is equally likely.

Unambiguous alphabets worth adopting rather than inventing

Every alphabet in the table below is citable to a primary source, which matters when someone asks why a code excludes a particular character. A house alphabet invented for one tool is neither interoperable nor citable, so there is no reason to build one.

The default in the box above is a 32-symbol set of uppercase letters and digits with 0, O, 1 and I removed. That is exactly 5 bits per symbol and 80 bits at sixteen characters. It removes the two cross-class confusions and keeps L and U, which Crockford Base32 also drops: L because it is confusable with the digit one, and U because Crockford removes it to avoid accidental obscenity. If the code will be read aloud or printed on something, take Crockford instead.

nanoid is the other source worth using directly. Its nolookalikes dictionary is 49 symbols and its nolookalikesSafe dictionary is 36 and contains no vowels, which makes accidental words impossible. Note that the nanoid default alphabet is not itself unambiguous, so do not reach for it expecting look-alikes to be gone.

Curated alphabets, verified character for character against their sources
AlphabetSymbolsSizeBits per symbolUse it for
nanoid nolookalikes346789ABCDEFGHJKLMNPQRTUVWXYabcdefghijkmnpqrtwxyz495.614710URLs people retype, invite codes, share links
Crockford Base320123456789ABCDEFGHJKMNPQRSTVWXYZ325.000000Voucher codes, licence keys, support PINs, anything read aloud
nanoid nolookalikesSafe6789BCDFGHJKLMNPQRTWbcdfghjkmnpqrtwz365.169925Mass-printed codes where an accidental word is a business risk
Digits only0123456789103.321928Keypad entry and phone-read codes, with the low entropy stated

What excluding look-alikes costs, exactly

Going from the 62-character alphanumeric alphabet to the 49-character nolookalikes alphabet loses 0.339486 bits per character. That is 5.70%. One extra character every 16.5 characters recovers it exactly, and the worked example checks out: sixteen alphanumeric characters carry 95.267 bits, and seventeen nolookalikes characters carry 95.450.

So the honest framing is that unambiguity is nearly free, provided you are willing to add a character. Of the fifteen generators audited, four offer some form of look-alike exclusion. One never says what it removes. One names a set in its label that does not match what it actually strips. Two name the set correctly but leave capital I in it, and none of the four shows the entropy cost. That is the gap this page fills: the set is named, the removed characters are printed, and the cost is on screen.

There is also a limit worth stating. The Unicode confusables data, UTS #39 version 17.0.0, contains exactly seven ASCII-to-ASCII confusable mappings, and several real confusions are between one character and two: rn against m, vv against w, cl against d. No amount of removing characters fixes those, so "removes all ambiguous characters" is an overclaim from any tool that says it.

The edge cases the field gets wrong

A single-symbol alphabet produces zero bits per character, and a string of any length from it carries no information at all. The tool refuses rather than emitting a run of the same character with a confident entropy number next to it.

Whitespace is a legal symbol and an invisible one. A space pasted into the alphabet will appear in the output where you cannot see it, and will usually survive a copy and break something later. The parsed symbol list shows whitespace explicitly for this reason.

Alphabet size has no practical ceiling here. The sampler draws 32-bit words and rejects against the largest multiple of the alphabet size, so any size up to 2 to the 32 minus 1 is handled uniformly with a single code path. Where the size is a power of two, 2, 16, 32 or 64, nothing is ever rejected, which is the reason those sizes keep appearing in encoding specifications.

Doing this in your own code

JavaScript
// Parse the alphabet into grapheme clusters, then deduplicate.
// Both steps are needed and both are usually skipped.
function parseAlphabet(input) {
  const seg = new Intl.Segmenter(undefined, { granularity: "grapheme" });
  const symbols = [...seg.segment(input)].map((s) => s.segment);
  return [...new Set(symbols)];
}

parseAlphabet("aab").length;      // 2, not 3
parseAlphabet("ab").length;       // 2 - same alphabet, same entropy
[..."key"].length;                // code points work for emoji
"key".length;                     // but .length counts UTF-16 units

Intl.Segmenter handles a base letter plus a combining mark as one symbol. Spreading into code points does not.

JavaScript, the failure
// What a naive implementation does with a custom set of "aab":
const alphabet = "aab";
alphabet[Math.floor(Math.random() * alphabet.length)];
// P(a) = 2/3, P(b) = 1/3

// Shannon entropy of that draw:
//   -(2/3)log2(2/3) - (1/3)log2(1/3) = 0.9183 bits
// A generator that counts distinct symbols for the entropy display but
// indexes the raw string for sampling will report 1.000 bits instead.
Python 3.9+
import secrets

def parse_alphabet(s: str) -> list[str]:
    # dict.fromkeys deduplicates and preserves order.
    # Iterating a str yields code points, so emoji stay intact.
    return list(dict.fromkeys(s))

alphabet = parse_alphabet("ABCDEFGHJKLMNPQRSTUVWXYZ23456789")
"".join(secrets.choice(alphabet) for _ in range(16))

Python has no grapheme segmentation in the standard library. For combining marks, normalise with unicodedata.normalize("NFC", s) first. The list[str] annotation needs Python 3.9; on 3.7 and 3.8 drop it or add from __future__ import annotations.

Go
func parseAlphabet(s string) []rune {
    seen := make(map[rune]bool)
    out := make([]rune, 0, len(s))
    for _, r := range s {          // ranges by rune, not byte
        if !seen[r] {
            seen[r] = true
            out = append(out, r)
        }
    }
    return out
}
Curated alphabets
// nanoid-dictionary 5.0.0, verbatim. Do not retype these from memory.
const nolookalikes     = "346789ABCDEFGHJKLMNPQRTUVWXYabcdefghijkmnpqrtwxyz";  // 49
const nolookalikesSafe = "6789BCDFGHJKLMNPQRTWbcdfghjkmnpqrtwz";                // 36

// Crockford Base32. Excludes I, L, O and U. Exactly 5 bits per character.
const crockford32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";                         // 32

// nanoid's own default alphabet is NOT unambiguous:
const nanoidDefault = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";

A bespoke house alphabet is non-interoperable and uncitable. Adopt one of these instead of inventing one.

Questions

What happens if I paste the same character twice?

It is counted once. Sampling from the raw string would make repeated symbols more likely and would make the entropy figure wrong: an alphabet of aab draws a two times in three, which is 0.9183 bits per character rather than 1.000. Deduplicating first keeps the draw uniform and keeps the displayed entropy true.

Can I use emoji or non-Latin characters in the alphabet?

Yes. The alphabet is segmented into grapheme clusters, so an emoji, a Cyrillic letter or a base letter with a combining mark each count as one symbol and come out intact. The parsed symbol count is shown so you can confirm the tool read your set the way you meant it.

Which unambiguous alphabet should I choose?

If the code appears in a URL people retype, use nanoid nolookalikes at 49 symbols. If it will be printed or read aloud, use Crockford Base32 at 32 symbols and exactly 5 bits each. If an accidental word would be a problem, use nolookalikesSafe, which has no vowels. All three are citable to a primary source, which is worth more than a set someone invented.

How much entropy do I lose by removing look-alike characters?

0.339486 bits per character going from 62 symbols to 49, which is 5.70%. Adding one character for every 16.5 characters of length recovers it exactly: a 16-character alphanumeric string is 95.267 bits and a 17-character nolookalikes string is 95.450.

Is there a maximum alphabet size?

Nothing you will reach. The sampler draws 32-bit words and rejects against the largest multiple of the alphabet size, so any size up to 4,294,967,295 symbols is sampled uniformly by the same code. Sizes that are powers of two reject nothing at all.

Why does the tool refuse a one-character alphabet?

Because the output would carry zero bits and the honest entropy display would read 0.00 regardless of length. Rather than produce a run of identical characters that looks like a generated value, it says so. The same feasibility logic applies to the unique-output option, which checks whether the request is possible before starting.

Related generators