ROT13 in Programming — Implementing and Using ROT13 in Code
ROT13 is simple to implement but appears in real codebases for obfuscation, puzzle games, and filter evasion. Here's how to implement ROT13 in JavaScript, Python, and Go — and...
ROT13 is one of the simplest cipher algorithms — rotate each letter 13 positions. Because the English alphabet has 26 letters, applying ROT13 twice returns the original text. Here’s clean implementations in several languages.
Use the ROT13 Cipher to encode and decode ROT13 text online.
JavaScript implementations
Concise regex version
const rot13 = str =>
str.replace(/[a-zA-Z]/g, c => {
const base = c <= 'Z' ? 65 : 97;
return String.fromCharCode(((c.charCodeAt(0) - base + 13) % 26) + base);
});
rot13('Hello World'); // 'Uryyb Jbeyq'
rot13('Uryyb Jbeyq'); // 'Hello World' (same function decodes)
Readable version
function rot13(text) {
return text.split('').map(char => {
const code = char.charCodeAt(0);
// Uppercase A-Z (65-90):
if (code >= 65 && code <= 90) {
return String.fromCharCode(((code - 65 + 13) % 26) + 65);
}
// Lowercase a-z (97-122):
if (code >= 97 && code <= 122) {
return String.fromCharCode(((code - 97 + 13) % 26) + 97);
}
// Non-letter: pass through unchanged
return char;
}).join('');
}
Lookup table version
const ROT13_TABLE = {};
'abcdefghijklmnopqrstuvwxyz'.split('').forEach((c, i) => {
const rotated = 'abcdefghijklmnopqrstuvwxyz'[(i + 13) % 26];
ROT13_TABLE[c] = rotated;
ROT13_TABLE[c.toUpperCase()] = rotated.toUpperCase();
});
const rot13 = str =>
str.split('').map(c => ROT13_TABLE[c] || c).join('');
The lookup table is faster than the arithmetic version for repeated calls.
Python implementations
Using codecs (built-in)
import codecs
rot13_text = codecs.encode('Hello World', 'rot_13')
# 'Uryyb Jbeyq'
# Decode (same operation):
codecs.encode('Uryyb Jbeyq', 'rot_13')
# 'Hello World'
# Or use decode:
'Hello World'.encode().decode('rot_13')
# AttributeError: Python 3 str doesn't have rot_13 codec on str directly
# Use codecs.encode instead
Manual implementation
def rot13(text: str) -> str:
result = []
for char in text:
if 'a' <= char <= 'z':
result.append(chr((ord(char) - ord('a') + 13) % 26 + ord('a')))
elif 'A' <= char <= 'Z':
result.append(chr((ord(char) - ord('A') + 13) % 26 + ord('A')))
else:
result.append(char)
return ''.join(result)
rot13('Hello World') # 'Uryyb Jbeyq'
rot13('Uryyb Jbeyq') # 'Hello World'
Using str.translate
import string
def make_rot13_table():
lower = string.ascii_lowercase
upper = string.ascii_uppercase
rotated_lower = lower[13:] + lower[:13]
rotated_upper = upper[13:] + upper[:13]
return str.maketrans(lower + upper, rotated_lower + rotated_upper)
ROT13_TABLE = make_rot13_table()
def rot13(text: str) -> str:
return text.translate(ROT13_TABLE)
# Using str.translate is faster than character-by-character for long strings
rot13('Hello World') # 'Uryyb Jbeyq'
Go implementation
package main
import "unicode"
func ROT13(s string) string {
return strings.Map(func(r rune) rune {
switch {
case r >= 'A' && r <= 'Z':
return 'A' + (r-'A'+13)%26
case r >= 'a' && r <= 'z':
return 'a' + (r-'a'+13)%26
}
return r
}, s)
}
Where ROT13 appears in real code
Test data obfuscation
// Obscure sensitive-looking test strings in source:
const TEST_PASSWORD = rot13('cnffjbeq123!'); // 'password123!'
const TEST_EMAIL = rot13('grfg@rknzcyr.pbz'); // 'test@example.com'
Prevents tools from flagging hardcoded credentials in test files.
Puzzle and game code
// In a game or ARG:
const SECRET_MESSAGE = 'Gur nafjre vf 42'; // 'The answer is 42'
function decode(msg) {
return rot13(msg); // Players discover this function
}
Avoiding string detection
# Avoid keyword filters in technical docs:
FILTER_KEYWORD = rot13('ivnten') # 'viagra' — demo of spam evasion concept
In configuration or data storage
Some older systems use ROT13 to “hide” config values from casual viewers (not secure, just obfuscated).
ROT13 variants
// ROT5 for digits (rotate 0-9 by 5):
const rot5 = str =>
str.replace(/[0-9]/g, n =>
String.fromCharCode(((n.charCodeAt(0) - 48 + 5) % 10) + 48)
);
// ROT18 = ROT13 for letters + ROT5 for digits:
const rot18 = str => rot13(rot5(str));
// ROT47 for all printable ASCII (33-126):
const rot47 = str =>
str.replace(/[!-~]/g, c =>
String.fromCharCode(((c.charCodeAt(0) - 33 + 47) % 94) + 33)
);
rot47('Hello World!'); // 'w6==@ (@C=5P'
Related tools
- ROT13 Cipher — encode and decode ROT13 online
- ROT13 Uses — where ROT13 is actually used
- Caesar Cipher and ROT13 — cipher comparison
Related posts
- Caesar Cipher and ROT13 — How Shift Ciphers Work — The Caesar cipher shifts each letter by a fixed number. ROT13 shifts by 13. Neit…
- Caesar Cipher Explained — ROT13, ROT47, and Shift Ciphers — The Caesar cipher shifts letters by a fixed number of positions. ROT13 is a Caes…
- ROT13 Uses — When and Why ROT13 Encoding Is Used — ROT13 rotates each letter by 13 positions in the alphabet. It's not encryption b…
- ROT13 Encoder — Encode and Decode Text with ROT13 — ROT13 shifts each letter 13 positions forward in the alphabet. It's its own inve…
Related tool
Encode and decode ROT13 and arbitrary Caesar shifts. Letter frequency analysis. 100% client-side.
Written by Mian Ali Khalid. Part of the Encoding & Crypto pillar.