JavaScript Tutorial

Letters to Numbers in JavaScript

AlphaCoder Team|May 24, 2026|10 min read

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;  // → 13

Node.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 Uint8Array for 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)).

Related Articles