Chaocipher
Encrypt WELLDONEISBETTERTHANWELLSAID with Byrne's classic starting disks and you get OAHQHCNYNXTSZJRRHJBYHQKSOUJY: both alphabets reshuffle after each letter, so every letter uses a different mapping. The same starting disks decrypt it. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Worked example
- Text
- WELLDONEISBETTERTHANWELLSAID
- Left
- HXUCZVAMDSLKPEFJRIGTWOBNYQ
- Right
- PTLNBQDEOYSFAVZKGJRIHWXUMC
- Calculation mode
- Encrypt
- Result
- OAHQHCNYNXTSZJRRHJBYHQKSOUJY
- Input
- WELLDONEISBETTERTHANWELLSAID
- Normalized
- WELLDONEISBETTERTHANWELLSAID
- Text
- OAHQHCNYNXTSZJRRHJBYHQKSOUJY
Chaocipher is the classical polyalphabetic machine invented by John F. Byrne in 1918 and kept secret for decades until the full algorithm was published in 2010. Unlike a fixed Caesar or Vigenère table, it keeps two circular alphabets—the left disk for ciphertext and the right disk for plaintext—and after every single letter both disks are rearranged by a strict zenith-and-nadir permutation. You supply the starting left and right alphabets as full 26-letter permutations of A–Z, the text to protect or recover, and a mode of encrypt or decrypt. The solver finds each letter on one disk, reads the aligned letter on the other, then rotates and slides both alphabets so the next mapping is completely different.
How the two alphabets and zenith–nadir rules work
Chaocipher encryption treats the right alphabet as the plaintext disk and the left alphabet as the ciphertext disk. For each plaintext letter you locate that letter in the current right alphabet and read the letter at the same index from the left alphabet; that is the ciphertext symbol for this step. Immediately afterward both alphabets are permuted with the stationary-zenith procedure used in Byrne’s model and documented by Moshe Rubin.
On the left disk the just-used ciphertext letter is rotated to the zenith (first position), the letter at zenith plus one is extracted, the block from zenith plus two through the nadir (position fourteen, zero-based index thirteen) shifts one place toward the zenith, and the extracted letter is reinserted at the nadir.
On the right disk the plaintext letter is rotated to the zenith, the whole alphabet is then rotated one more step so the next letter sits at zenith, the letter at zenith plus two is extracted, the block from zenith plus three through the nadir shifts left, and the extracted letter lands at the nadir. Decryption uses the inverse lookup—find the ciphertext letter on the left disk and read the same-index letter on the right—then applies exactly the same two permutations so the machine state stays synchronized.
Because each step depends on the previous letter, a single-letter change early in the message cascades through both alphabets and destroys long runs of monoalphabetic structure, which is why classical frequency analysis fails on long Chaocipher traffic when the starting disks are unknown.
Practical tips, limits, and related searches
Use well-shuffled starting alphabets rather than near-sequential A–Z strings so the early permutations do not leave long alphabetical runs near the zenith. The classic exhibit pair left HXUCZVAMDSLKPEFJRIGTWOBNYQ and right PTLNBQDEOYSFAVZKGJRIHWXUMC with plaintext WELLDONEISBETTERTHANWELLSAID is the standard self-check: encryption must yield OAHQHCNYNXTSZJRRHJBYHQKSOUJY, and decryption of that stream with the same starting disks must recover the original phrase. Round-trip checks are the best sanity test when you invent new disks—encrypt, then decrypt with the original left and right strings, never with the final alphabets from a previous run.
Prefer this capability when you need transparent classical dynamic substitution for education or puzzles; if you need real secrecy, use a modern authenticated cipher instead.
What you can do with it
CTF and puzzle write-ups
Challenge authors verify Chaocipher encrypt and decrypt steps for published starting disks without maintaining a separate offline script.
Historical crypto demos
Museums and blog demos show how evolving dual alphabets differ from fixed monoalphabetic ciphers by live-encrypting visitor phrases with shared left and right disks.
FAQ
What is Chaocipher?
A classical polyalphabetic cipher by John F. Byrne (1918). Two alphabets align each letter, then both permute after every character so the substitution never stays fixed.
What are the left and right alphabets?
The left disk supplies ciphertext letters; the right disk holds plaintext letters. Each must be a full permutation of A–Z (26 unique letters). They are the machine’s secret key.
What happens if an alphabet is malformed?
Wrong length, duplicate letters, missing A–Z symbols, or non-letter characters in the disk string are all rejected with a clear message.
How does decryption stay in sync?
Decrypt looks up the ciphertext letter on the left disk and reads the same-index letter on the right, then applies the same left and right permutations used when encrypting.
Is Chaocipher free?
The browser tool is free and runs locally.
Related tools
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/enc/chaocipher \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"WELLDONEISBETTERTHANWELLSAID","left":"HXUCZVAMDSLKPEFJRIGTWOBNYQ","right":"PTLNBQDEOYSFAVZKGJRIHWXUMC"}'const res = await fetch("https://api.kit.forhosting.com/enc/chaocipher", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"text": "WELLDONEISBETTERTHANWELLSAID",
"left": "HXUCZVAMDSLKPEFJRIGTWOBNYQ",
"right": "PTLNBQDEOYSFAVZKGJRIHWXUMC"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/enc/chaocipher",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"text": "WELLDONEISBETTERTHANWELLSAID",
"left": "HXUCZVAMDSLKPEFJRIGTWOBNYQ",
"right": "PTLNBQDEOYSFAVZKGJRIHWXUMC"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/enc/chaocipher", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"text":"WELLDONEISBETTERTHANWELLSAID","left":"HXUCZVAMDSLKPEFJRIGTWOBNYQ","right":"PTLNBQDEOYSFAVZKGJRIHWXUMC"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"text":"WELLDONEISBETTERTHANWELLSAID","left":"HXUCZVAMDSLKPEFJRIGTWOBNYQ","right":"PTLNBQDEOYSFAVZKGJRIHWXUMC"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/enc/chaocipher", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"text": "WELLDONEISBETTERTHANWELLSAID",
"left": "HXUCZVAMDSLKPEFJRIGTWOBNYQ",
"right": "PTLNBQDEOYSFAVZKGJRIHWXUMC"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "enc.chaocipher",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
Technical notes
Malformed alphabets—wrong length, duplicate letters, missing A–Z symbols, or non-letter junk—are rejected with a clear invalid_input error instead of silently producing nonsense.
How to call the API and what comes back
Send a JSON body with three required string fields—text, left, and right—plus an optional mode that defaults to encrypt when omitted. Accepted aliases for text include input, value, data, message, plaintext, and ciphertext; left also accepts left_alphabet, left_disk, ct_alphabet, and cipher_alphabet; right accepts right_alphabet, right_disk, pt_alphabet, and plain_alphabet; mode accepts operation, op, direction, and action, and recognizes common synonyms such as encode or decode. Both alphabets must be exactly twenty-six unique A–Z letters after optional separators are ignored; a short string, a run of duplicates, a missing letter, or a digit inside the disk string is invalid_input and is never charged. Text is normalized to uppercase A–Z only; spaces and punctuation are stripped, and a letter-free payload is rejected. On success the response echoes the original text and the starting left and right alphabets, reports mode as encrypt or decrypt, normalized as the letter-only stream actually processed, result and text as the ciphertext or recovered letters, length as the output character count, left_final and right_final as the alphabets after the last permutation, formula as a short rule mnemonic, and message as a one-line English summary. Pricing is a flat $0.002 per successful request with unit item.
Practical tips, limits, and related searches
Keep payloads under the published maximum of one hundred thousand characters so Worker memory stays bounded. When teaching, pair three examples: a short phrase with the Byrne exhibit alphabets, a decrypt of a published ciphertext, and a deliberately malformed alphabet to show the hard invalid_input path. This landing targets people searching for Chaocipher, Byrne cipher, or two-disk evolving alphabet tools online; it is not AES, not a password vault, not Playfair, and not a simple Caesar shift. Failed tasks that return invalid_input do not consume prepaid balance, which keeps exploratory scripts cheap when clients probe broken alphabets.
Integration use cases
Classroom classical-cipher labs
Students set the Byrne exhibit alphabets by hand, encrypt a short motto, then compare the letter stream and final disks against the deterministic API result.