JavaScript's built-in string methods make letter-to-number conversion simple and efficient. The charCodeAt() method returns the UTF-16 code unit of a character, and String.fromCharCode() converts numbers back to letters. This guide walks through every technique from basic conversions to building complete interactive cipher tools in the browser.
The Basics: charCodeAt() and fromCharCode()
The charCodeAt(0) method returns the numeric value of the first character in a string. For English letters, this matches ASCII values: uppercase A is 65, Z is 90.
// Letter to position number
const position = letter.toUpperCase().charCodeAt(0) - 64;
// Examples
"A".charCodeAt(0) - 64 // → 1
"Z".charCodeAt(0) - 64 // → 26
"m".toUpperCase().charCodeAt(0) - 64 // → 13
// Position number to letter
const letter = String.fromCharCode(position + 64);
String.fromCharCode(1 + 64) // → "A"
String.fromCharCode(26 + 64) // → "Z"
String.fromCharCode(13 + 96) // → "m" (lowercase)Converting a Full String
Using the Spread Operator and map()
function encodeA1Z26(text, sep = "-") {
return [...text.toUpperCase()]
.filter(c => c >= "A" && c <= "Z")
.map(c => c.charCodeAt(0) - 64)
.join(sep);
}
encodeA1Z26("Hello"); // → "8-5-12-12-15"
encodeA1Z26("Hello", ","); // → "8,5,12,12,15"Preserving Word Boundaries
function encodeWithWords(text, sep = "-", wordSep = " / ") {
return text
.split(/\s+/)
.map(word =>
[...word.toUpperCase()]
.filter(c => c >= "A" && c <= "Z")
.map(c => c.charCodeAt(0) - 64)
.join(sep)
)
.filter(w => w.length > 0)
.join(wordSep);
}
encodeWithWords("Hello World");
// → "8-5-12-12-15 / 23-15-18-12-4"Decoding Numbers to Letters
function decodeA1Z26(encoded, sep = "-", wordSep = " / ") {
return encoded
.split(wordSep)
.map(word =>
word
.split(sep)
.map(n => {
const num = parseInt(n.trim(), 10);
return num >= 1 && num <= 26
? String.fromCharCode(num + 64)
: "";
})
.join("")
)
.join(" ");
}
decodeA1Z26("8-5-12-12-15 / 23-15-18-12-4");
// → "HELLO WORLD"
decodeA1Z26("16,25,20,8,15,14", ",");
// → "PYTHON"TypeScript Version
For TypeScript projects, add type annotations for better safety:
function encodeA1Z26(text: string, sep: string = "-"): string {
return [...text.toUpperCase()]
.filter((c: string): boolean => c >= "A" && c <= "Z")
.map((c: string): number => c.charCodeAt(0) - 64)
.join(sep);
}
function decodeA1Z26(
encoded: string,
sep: string = "-",
wordSep: string = " / "
): string {
return encoded
.split(wordSep)
.map((word: string): string =>
word
.split(sep)
.map((n: string): string => {
const num = parseInt(n.trim(), 10);
return num >= 1 && num <= 26
? String.fromCharCode(num + 64)
: "";
})
.join("")
)
.join(" ");
}Building an Interactive Converter
Here is a minimal HTML + JavaScript implementation of a live converter that runs entirely in the browser:
<textarea id="input" placeholder="Type text here..."></textarea>
<div id="output"></div>
<script>
const input = document.getElementById("input");
const output = document.getElementById("output");
input.addEventListener("input", () => {
const text = input.value;
const encoded = [...text.toUpperCase()]
.filter(c => c >= "A" && c <= "Z")
.map(c => c.charCodeAt(0) - 64)
.join("-");
output.textContent = encoded;
});
</script>This creates a real-time converter where every keystroke immediately updates the output. The entire conversion happens client-side with zero server requests.
Word Value Calculator
function wordValue(text) {
return [...text.toUpperCase()]
.filter(c => c >= "A" && c <= "Z")
.reduce((sum, c) => sum + c.charCodeAt(0) - 64, 0);
}
wordValue("HELLO"); // → 52 (8+5+12+12+15)
wordValue("PYTHON"); // → 98 (16+25+20+8+15+14)
wordValue("CODE"); // → 27 (3+15+4+5)Modern JavaScript: Using codePointAt()
For modern browsers and Node.js, codePointAt() is preferred over charCodeAt() because it correctly handles supplementary Unicode characters (emoji, CJK extensions). For English letters, both return identical values:
// Both return 65 for "A"
"A".charCodeAt(0) // → 65
"A".codePointAt(0) // → 65
// For A1Z26, either works
const pos = "M".codePointAt(0) - 64; // → 13Node.js: Processing Files
const fs = require("fs");
function encodeFile(inputPath, outputPath) {
const text = fs.readFileSync(inputPath, "utf-8");
const lines = text.split("\n").map(line =>
encodeWithWords(line)
);
fs.writeFileSync(outputPath, lines.join("\n"));
}
// Encode a file
encodeFile("message.txt", "encoded.txt");Performance Tips
- Avoid repeated string concatenation. Use arrays with
.join()instead of building strings with+=. - Pre-compute lookup tables if converting millions of characters:
const table = Object.fromEntries([...Array(26)].map((_, i) => [String.fromCharCode(65 + i), i + 1])) - Use
Uint8Arrayfor extreme performance with large binary buffers in Node.js.
See it in action: Our A1Z26 Cipher Converter is built with these exact JavaScript techniques. Try it now — no installation needed.
Frequently Asked Questions
What is the difference between charCodeAt() and codePointAt()?
charCodeAt() returns a UTF-16 code unit (0-65535). codePointAt() returns the full Unicode code point, correctly handling characters outside the Basic Multilingual Plane (like emoji). For English letters, both return the same ASCII values. Use codePointAt() for Unicode-safe code.
Does this work with template literals?
Yes. Template literals are regular strings in JavaScript, so charCodeAt() works normally: `Hello`.charCodeAt(0) returns 72 (the code for H).
How do I handle user input with special characters?
Filter the input to include only alphabetic characters before conversion. The .filter(c => c >= "A" && c <= "Z") pattern shown in the examples handles this. You can also use a regex test: .filter(c => /[A-Z]/.test(c)).