Skip to main content
Skip to main content
Keys & Seeds

Generate Your Own Bitcoin Seed Phrase with Dice

Most wallets generate seed phrases for you, but that requires trust: you are trusting that the wallet's random number generator is actually random, hasn't been backdoored, and wasn't compromised somewhere in the supply chain. This guide shows you how to generate a 24-word BIP39 seed phrase from dice rolls, entirely offline, with randomness you can verify yourself.

Fund Loss Warning

Mistakes in seed generation can result in permanent, irreversible loss of all Bitcoin.

Common fatal errors:

  • Using a compromised computer: If your "air-gapped" machine was ever connected to the internet, it may have malware that captures your seed.
  • Insufficient randomness: Using weak entropy (like mental "random" numbers) makes your seed guessable.
  • Transcription errors: A single wrong word means a completely different (empty) wallet.
  • Improper backup storage: Paper burns, fades, and water-damages easily.

There is no recovery. No customer support. No password reset. If you lose access to your seed or generate it insecurely, your Bitcoin is gone forever.

Do not proceed unless you fully understand these risks.

What You'll Do

In this guide you will:

  • Generate true randomness using physical dice rolls
  • Convert your binary entropy into decimal numbers
  • Calculate the checksum that completes the final word
  • Look up all 24 BIP39 seed words
  • Verify the finished seed in an offline wallet
  • Securely back up your seed phrase on metal

Time required: 2โ€“4 hours Difficulty: Intermediate Estimated cost: $10โ€“30 (casino dice) + $20โ€“50 (metal backup plate) Requirements: Air-gapped computer or Raspberry Pi Zero, casino dice

Prerequisites

Before starting, make sure you understand:

Why Generate Your Own Seed?โ€‹

When a wallet generates a seed phrase for you, you are trusting three things at once:

RiskDescription
Weak randomnessSoftware may not use proper entropy
BackdoorsWallets could have security flaws or intentional vulnerabilities
Supply chain attacksPre-generated seeds have been found in compromised hardware wallets

By generating your own seed with physical dice, you:

  • Verify the randomness yourself: no trust required
  • Eliminate software vulnerabilities: dice can't be hacked
  • Understand what you're protecting: knowledge is security

Who Is This Guide For?โ€‹

SituationRecommendation
Learning with small amountsNot recommended: Use a hardware wallet's built-in seed generation
Moderate holdings, want to learnMaybe: Practice on testnet first and understand the risks
Significant holdings, high security needsYes: Verifiable entropy is worth the effort
Don't trust hardware wallet RNGYes: This eliminates that trust requirement
Not comfortable with technical processesNo: A mistake here loses everything

Most people should use their hardware wallet's seed generation. This guide is for users who want verifiable randomness and understand the additional risks of a manual process.

Critical Environment Requirementsโ€‹

Before You Begin

Your seed generation environment must meet ALL of these requirements:

  • Air-gapped computer: A machine that has NEVER connected to the internet and NEVER will
  • Fresh operating system: Booted from a verified, read-only medium (like a Tails USB)
  • No wireless hardware: Wi-Fi and Bluetooth physically removed or disabled in BIOS
  • No cameras or microphones: Cover or disconnect them
  • Private location: No one can see your screen or your seed words
  • No electronic devices nearby: Phones, smartwatches, etc. can capture keystrokes or screens

If any of these are not met, your seed may be compromised before you even finish generating it.

What You'll Needโ€‹

Casino diceโ€‹

Casino-grade dice are precision-machined with flush, filled pips, so every face weighs the same. Cheaper pipped dice have material drilled out for each pip, which makes some faces very slightly more likely than others.

That bias is small and not fatal here: across 256 bits, a fraction-of-a-percent bias per face costs a negligible amount of entropy. Casino dice simply remove the question, and they are cheap.

More dice is about speed, not fairness

Rolling several dice at once does not cancel out a die's bias. Each die still produces its own slightly biased bit. Using 5โ€“10 dice per throw is purely a speed optimization: it collects 5โ€“10 bits per throw instead of 1.

Dice

Air-gapped computerโ€‹

An air-gapped computer is a device that has never been connected to the internet and is physically incapable of doing so. This is crucial for securely generating and handling your private key. Suitable options:

  • Raspberry Pi Zero 1.3: Highly recommended because it lacks built-in Wi-Fi and Bluetooth, reducing attack surfaces. Harder to find these days, but worth the hunt.
  • Laptop or PC: A machine with the Wi-Fi and Bluetooth modules physically removed and the Ethernet port permanently disabled. Even if stolen, an attacker cannot put it back online to extract the private key.

You genuinely need a computer for this process. Step 1, 2, and 4 can be done with paper and pen, but Step 3 computes a SHA-256 hash, which cannot be done by hand.

Raspberry Pi Zero

Paper and penโ€‹

Used only as a temporary record of the process. Must be destroyed (burned) after you have transferred the seed phrase to a durable medium.

Metal seed storageโ€‹

Once your seed phrase is created, it should be permanently stored on a fireproof, waterproof, tamper-resistant metal plate. This protects your seed from fire, water, and the slow degradation of paper over time.

Metal seed backup

How the Numbers Fit Togetherโ€‹

Before you start rolling, it helps to see where the process is going. A 24-word BIP39 seed is 264 bits, and those bits come from two different places:

Where it comes fromBitsStep
Dice rolls (your entropy)256Step 1
Checksum, calculated from those 256 bits8Step 3
Total264264 รท 11 = 24 words

You will write your 256 dice bits into a grid of 24 rows, 11 bits per row. That fills 23 rows completely (23 ร— 11 = 253) and leaves 3 bits in row 24. Row 24 stays unfinished until Step 3, when the checksum supplies its last 8 bits.

This is why the steps must be done in order: row 24 does not have a value until Step 3.

Step 1: Roll Dice for Entropyโ€‹

Your goal is to generate a large, truly random binary number by rolling dice.

Assign binary valuesโ€‹

Before rolling, decide how each die is converted into a binary value. This mapping gives an equal probability of 0 or 1:

  • If the die lands on 1, 2, or 3, it is assigned a value of 0.
  • If the die lands on 4, 5, or 6, it is assigned a value of 1.

Roll and recordโ€‹

You need 256 individual die results, one per bit. With 5 dice that is about 52 throws; with 10 dice, about 26. Stop once you have 256 bits and discard any extras from the final throw.

Roll the dice and record the results from left to right. Consistency is key: always read in the same order. If it is unclear which die is further to the left, re-roll those dice.

warning

It is crucial that the data is truly random. If it lacks randomness, there is a risk that someone else could reproduce the exact same sequence. This would allow them to regenerate your private key and potentially access all of your Bitcoin.

Format the outputโ€‹

Write your 256 bits into 23 rows of 11 binary digits, plus a 24th row that will hold only 3 digits for now.

  • For readability, separate each full row into three groups: 4-4-3 (e.g., 1011 1000 101).
  • Keep the numbers aligned in columns and leave space to the right of each row for the calculations in Step 2.
  • Leave the rest of row 24 blank. Its final 8 digits are filled in by the checksum in Step 3.
#
1)10111000101
2)11011100100
3)10111100111
4)01101000101
5)11111010101
6)11111001000
7)00100000101
8)10111000101
9)10101010100
10)10111011101
11)11111000101
12)01111001110
13)10111010100
14)10011000111
15)10111000111
16)11111011101
17)00011000101
18)10101000110
19)11111010100
20)11111000100
21)00101000101
22)10111010101
23)10011000101
24)001

You have now generated your 256 bits of entropy: 23 full rows of 11 bits, plus 3 bits in row 24.

Do not roll dice for the remaining 8 bits. They are not random data. They are the checksum, and they must be calculated from the 256 bits above so your wallet can detect typos later.

Step 2: Convert Binary to Decimalโ€‹

Convert rows 1 through 23 to decimal. Each 11-bit row becomes one number, which you will use in Step 4 to look up a seed word.

Row 24 is not ready yet

Row 24 still has only 3 of its 11 bits, so it has no value at this stage. You will complete and convert it in Step 3. Skip it for now.

Do the conversion manually on your air-gapped computer or with paper and pen. Never use an online tool. Copying your binary string into a web calculator could expose your seed.

With 11 binary digits, the smallest number is 0 (00000000000) and the largest is 2047 (11111111111). Each result will therefore fall in the range 0โ€“2047.

You can convert in either of two ways.

Method A: air-gapped shellโ€‹

In a bash terminal, to convert the first row, 10111000101, type:

echo $((2#10111000101))

This will output 1477. Replace the binary digits in the command with each 11-digit row and run the calculation.

Method B: paper and penโ€‹

At the top-left of your page, write the powers of two from left to right, aligned with the binary digits below: 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1.

For each binary digit in the row:

  • If the digit is 1, write down the power of two above it.
  • If the digit is 0, skip it.

Add up all the numbers you wrote down. The sum is the decimal equivalent of the binary number.

For example, to convert the first row, 10111000101:

1024 + 0 + 256 + 128 + 64 + 0 + 0 + 0 + 4 + 0 + 1 = 1477

Repeat this for rows 1 through 23. You will have 23 decimal numbers, each in the range 0โ€“2047.

#10245122561286432168421TOT
1)101110001011477
2)110111001001764
3)101111001111511
4)01101000101837
5)111110101012005
6)111110010001992
7)00100000101261
8)101110001011477
9)101010101001364
10)101110111011501
11)111110001011989
12)01111001110974
13)101110101001492
14)100110001111223
15)101110001111479
16)111110111012013
17)00011000101197
18)101010001101350
19)111110101002004
20)111110001001988
21)00101000101325
22)101110101011493
23)100110001011221
24)001โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“Step 3

Step 3: Calculate the Checksumโ€‹

The 8 missing digits in row 24 are calculated from the 256 bits you rolled in Step 1. Together they form a checksum: a short verification code that lets your wallet detect if you have made a typo when entering the seed. If the checksum doesn't match, the wallet warns you that something is wrong.

Two ways to do this

The method below is manual: you hash your bits, then convert two hexadecimal characters by hand. It is the one that shows you what a checksum actually is, and it is worth doing at least once.

If you would rather not do the arithmetic, a single command at the end of this step produces the same result on the same air-gapped machine.

The safest option is both. Work it out by hand, then run the command and confirm the two agree. If they disagree, you made a mistake somewhere, stop and find it before going any further.

Write out your 256 bits as one lineโ€‹

Read your grid from Step 1 straight through, row 1 to row 24, left to right, with no spaces and no line breaks. Rows 1โ€“23 contribute 11 bits each and row 24 contributes its 3 bits, for 256 characters total.

For our example:

1011100010111011100100101111001110110100010111111010101111110010000010000010110111000101101010101001011101110111111000101011110011101011101010010011000111101110001111111101110100011000101101010001101111101010011111000100001010001011011101010110011000101001

Count the characters before continuing. If you don't have exactly 256, the hash will be wrong and so will your seed.

Generate the hash outputโ€‹

On your air-gapped Linux machine, feed that 256-bit string into SHA-256:

echo 1011100010111011100100101111001110110100010111111010101111110010000010000010110111000101101010101001011101110111111000101011110011101011101010010011000111101110001111111101110100011000101101010001101111101010011111000100001010001011011101010110011000101001 | shasum -a 256 -0

Our example output:

52831c8346d7423d26648b51490f2d7ae0ddf172956f241a6bb8bdc0d887c292 ^-
The -0 flag is not optional

-0 (also written --01) puts shasum into BITS mode, where each 0 and 1 character is treated as an actual bit. BIP39 requires hashing the 256 raw bits, not the text "1011โ€ฆ".

Drop the -0 and the command still runs and still prints a perfectly normal-looking hash, but it is the hash of 256 ASCII characters instead: 03ab8729โ€ฆ rather than 52831c83โ€ฆ. That produces a wrong checksum, a wrong 24th word, and an invalid seed phrase. The ^ in the output above is shasum confirming BITS mode was used.

If shasum is unavailable, this gives the same result:

python3 -c "import hashlib; b='YOUR_256_BITS'; print(hashlib.sha256(int(b,2).to_bytes(32,'big')).hexdigest())"

Running both and comparing is a good way to catch a mistyped bit string.

The checksum is the first 8 bits of that hash, which means you only care about the first two hexadecimal characters: 52.

Convert the first two hex characters to binaryโ€‹

Convert the two hexadecimal digits 5 and 2 into their 4-bit binary equivalents. Hexadecimal is a number system that uses 0โ€“9 plus aโ€“f to represent values 0โ€“15.

HEXDecimalBinary
000000
110001
220010
330011
440100
550101
660110
770111
881000
991001
a101010
b111011
c121100
d131101
e141110
f151111

From the table:

  • 5 in binary is 0101 (4 bits)
  • 2 in binary is 0010 (4 bits)

Concatenated, the checksum is 01010010 (8 bits).

Complete row 24โ€‹

Append those 8 bits to the 3 bits already in row 24. It now holds 11 bits and your grid is complete at 264 bits:

#
1)10111000101
2)11011100100
3)10111100111
4)01101000101
5)11111010101
6)11111001000
7)00100000101
8)10111000101
9)10101010100
10)10111011101
11)11111000101
12)01111001110
13)10111010100
14)10011000111
15)10111000111
16)11111011101
17)00011000101
18)10101000110
19)11111010100
20)11111000100
21)00101000101
22)10111010101
23)10011000101
24)00101010010

Now convert row 24 to decimal using the same method as Step 2. In our example, 00101010010 gives:

0 + 0 + 256 + 0 + 64 + 0 + 16 + 0 + 0 + 2 + 0 = 338

You now have all 24 decimal numbers: 23 from Step 2, plus 338 from row 24.

Or do it in one commandโ€‹

Everything above, the hash, the hex conversion, and row 24, can also be produced by one command on the same air-gapped machine. python3 ships with Raspberry Pi OS, Tails, and every mainstream Linux install, and neither command below needs an internet connection or a single package you have to install.

Start by putting your 256 bits into a variable. You can paste them with the 4-4-3 spacing from your grid, because both commands ignore whitespace:

BITS=1011100010111011100100101111001110110100010111111010101111110010000010000010110111000101101010101001011101110111111000101011110011101011101010010011000111101110001111111101110100011000101101010001101111101010011111000100001010001011011101010110011000101001

Option 1: just the checksum. This finishes row 24 and leaves the word lookups in Step 4 to you:

Just the Checksum

python3 -c "
import hashlib,sys
b=''.join(sys.argv[1].split())
if len(b)!=256 or set(b)-{'0','1'}:
sys.exit('ERROR: need exactly 256 binary digits, got %d' % len(b))
cs=format(hashlib.sha256(int(b,2).to_bytes(32,'big')).digest()[0],'08b')
print('checksum bits :',cs)
print('row 24 binary :',b[253:]+cs)
print('row 24 decimal:',int(b[253:]+cs,2))
" "$BITS"

For our example this prints:

checksum bits : 01010010
row 24 binary : 00101010010
row 24 decimal: 338

Option 2: all 24 numbers. This prints every row's decimal value, which lets you check all of Step 2 as well as Step 3:

All 24 Numbers

python3 -c "
import hashlib,sys,os
b=''.join(sys.argv[1].split())
if len(b)!=256 or set(b)-{'0','1'}:
sys.exit('ERROR: need exactly 256 binary digits, got %d' % len(b))
f=b+format(hashlib.sha256(int(b,2).to_bytes(32,'big')).digest()[0],'08b')
w=open('english.txt').read().split() if os.path.exists('english.txt') else []
for n in range(24):
i=int(f[n*11:n*11+11],2)
print('%2d. %4d %s' % (n+1,i,w[i] if w else ''))
" "$BITS"

If you have saved the official BIP39 word list as english.txt in the same folder, this also prints the words, giving you an independent check on the lookups you do in Step 4. Without that file it prints the numbers only, which is all you need.

Both commands refuse to guess

If your string is not exactly 256 binary digits, the command stops with an error instead of printing a plausible-looking wrong answer. Miscounting your bits is the most common mistake in this whole process, so let the machine catch it.

Why several last words can look "valid"โ€‹

You may have read that a 24-word seed has more than one possible last word. That is true, and it is the most confusing part of this process, so it is worth being exact about it.

Rows 1โ€“23 fix 253 of your 264 bits. The 24th word supplies the remaining 11: the 3 bits you rolled in row 24, plus the 8 checksum bits. Because those 3 rolled bits can take 8 different values, exactly 8 of the 2048 BIP39 words produce a valid checksum. For the example used throughout this guide, they are:

Row 24's 3 rolled bits24th word
000believe
001clean โ† what our dice rolled
010gap
011hover
100message
101rule
110soul
111visual

You do not choose between them. Your dice already chose.

The checksum never picks a word off that list. It completes the word your own 3 bits already started. We rolled 001, so our word is "clean". Had we rolled 110, the checksum bits would have come out differently too, and the word would have been "soul".

The other seven are valid, and no wallet will warn you

Each of those 8 words produces a perfectly valid BIP39 seed phrase. Pick the wrong one and nothing rejects it: you get a real, valid, completely different wallet that has nothing to do with the dice you rolled. There is no error message, because from the wallet's point of view nothing is wrong.

Never pick a last word from a list of candidates. If you rolled all 256 bits, exactly one word is yours, the one your own checksum produced.

Where the confusion comes from

Tools that offer you a menu of "valid last words" are built for a different starting point: someone who has 23 words and no entropy committed to row 24 yet. For them any of the 8 really is equally fine, because making that choice is how they supply the last 3 bits of entropy. You supplied those bits with dice in Step 1, so the choice is already spent.

The effect is far more visible with 12-word seeds. There the last word carries 7 entropy bits and only 4 checksum bits, so 128 of the 2048 words are valid last words. That is where most people first run into this idea, and it does not carry over to a dice-rolled seed where every bit of entropy is already fixed.

Step 4: Look Up BIP39 Wordsโ€‹

BIP39 (Bitcoin Improvement Proposal 39) defines a list of 2048 words, arranged alphabetically. Each word corresponds to a specific position, and each of your 24 decimal numbers points at one word.

  • The smallest possible value is 0 (binary 00000000000), which corresponds to the word "abandon", the first word on the list.
  • The largest possible value is 2047 (binary 11111111111), which corresponds to the word "zoo", the last word on the list.
Zero-indexed list, one-indexed GitHub

Computers count from 0. The BIP39 position of "abandon" is 0, not 1. However, the official BIP39 word list on GitHub displays line numbers starting from 1. So a BIP39 position of 1477 maps to GitHub line 1478. Always add 1 to your decimal when searching the GitHub file.

For example, the first row's decimal is 1477. On GitHub you will find it on line 1478: the word is "reward".

Look up each decimal value below (remembering to add 1 when searching GitHub) and record the corresponding word for all 24 rows.

#BIP39GitHubWord
1)14771478reward
2)17641765symptom
3)15111512rude
4)837838hamster
5)20052006wide
6)19921993weekend
7)261262camera
8)14771478reward
9)13641365pride
10)15011502roof
11)19891990weather
12)974975keep
13)14921493ritual
14)12231224ocean
15)14791480rib
16)20132014wing
17)197198board
18)13501351potato
19)20042005whisper
20)19881989weasel
21)325326chunk
22)14931494rival
23)12211222obvious
24)338339clean
Order is part of the seed

The words must stay in this exact order. The same 24 words in a different order is a completely different (and empty) wallet. Number every word as you write it down.

You have now created a 24-word Bitcoin mnemonic seed. The next step confirms it is valid.

Step 5: Verify Your Seed Phraseโ€‹

Install a software wallet such as Sparrow on your air-gapped computer and enter the 24 words in order.

  • If the wallet accepts the seed, the checksum inside the phrase is consistent. That is necessary but not sufficient, so continue to the re-derivation check below before you move on.
  • If the wallet rejects the seed, the checksum is wrong. Do not adjust words at random, and do not swap the last word for another one that happens to be accepted. Go back and re-check, in this order: the 256-bit string you hashed in Step 3 (exactly 256 characters, no typos), that you used the -0 flag, the two hex characters you converted, row 24's binary, and finally the word lookups.

Keep this wallet offline. It is being used to check the arithmetic, not to hold funds.

Acceptance alone does not prove the seed is yoursโ€‹

A wallet accepting your phrase proves the 24 words are consistent with each other. It does not prove they match the dice you rolled.

As Step 3 explained, 8 different last words all pass that check, and 7 of them open a different wallet. If you picked the wrong one, or mistyped a word into another one that keeps the checksum valid, Sparrow opens a valid wallet without a single warning. The only thing that catches this is comparing the result against your dice grid:

  1. Take the 256-bit string from your grid again.
  2. Run Option 2 from Step 3, or redo the lookups by hand on a clean sheet of paper.
  3. Compare all 24 words against what you entered into the wallet, in order, one at a time.

If every word matches, the seed really is the one your dice produced. If any word differs, trust the dice grid and find the mistake before you send any funds to it.

Importing the seed into Sparrow Wallet for verification

Step 6: Back Up on Metalโ€‹

Your Bitcoin seed phrase is the key to your funds. If it is lost or compromised, your Bitcoin is gone forever. Paper and digital backups can degrade, get lost, or be destroyed. A metal seed plate is a durable, fireproof, waterproof backup that can last decades.

Why metalโ€‹

  • Fire and water resistance: Unlike paper, metal plates withstand extreme temperatures and flooding.
  • Durability: Metal does not degrade over time the way paper and electronic storage do.
  • Tamper resistance: A sealed metal backup makes unauthorized access visible.
  • Longevity: A well-engraved or stamped metal seed plate can last a lifetime, keeping your Bitcoin recoverable for decades.

Finish in this orderโ€‹

  1. Stamp or engrave all 24 words onto the metal plate, numbered and in order.
  2. Read the metal plate back word by word against your paper and confirm every word and position matches.
  3. Only then, destroy every paper record of the process: the dice grid, the decimal calculations, and the word list. Burn them.
  4. Store the plate in a secure location: a safe, hidden vault, or safety deposit box.

Consider splitting your backup across multiple secure locations, and never store the seed digitally or photograph it.

Your Bitcoin is only as safe as your seed backup. Protect it wisely.


Related Topics

Next Steps