How Fancy Text Generators Work: Unicode Explained

Summary

Pixel’s announcement explains how fancy text generators replace ordinary letters with distinct Unicode characters rather than applying fonts. It explores the Mathematical Alphanumeric Symbols block, shows JavaScript conversion examples, and highlights edge cases that matter when building copyable text tools.

DEV Community

How Fancy Text Generators Actually Work (It’s Just Unicode)

You've seen it in Instagram bios, Twitter names and Discord servers: text like 𝐁𝐨𝐥𝐝, 𝓢𝓬𝓻𝓲𝓹𝓽 or 𝔾𝕠𝕥𝕙𝕚𝕔 in apps that don't let you pick a font at all. So how does it work? Short answer: it isn't a font. Fancy text generators swap each normal letter for a different Unicode character that happens to look styled. In this post I'll show you exactly which characters they use, the few lines of JavaScript that do the conversion, and the edge cases that broke my own generator when I built one. It's Not a Font, It's a Different Character A fancy text generator doesn't change how a letter looks. It replaces the letter with a completely separate character. A font is a visual skin applied to the same character, but 𝐀 and A are two different characters with two different code points. You can prove it in your browser console: console.log("𝐀" === "A"); // false
console.log("𝐀".codePointAt(0).toString(16)); // "1d400"
U+1D400 is officially named MATHEMATICAL BOLD CAPITAL A. Because it's a real character and not styling, it survives copy and paste. That's why it works in places with no formatting options: an Instagram bio, a username field, a plain . Where These Characters Come From: The Mathematical Alphanumeric Symbols Block Most "fonts" in a fancy text generator come from one Unicode block called Mathematical Alphanumeric Symbols, which runs from U+1D400 to U+1D7FF. It was added in Unicode 3.1 (2001) for mathematicians, not for social media. In math notation, a bold x, an italic x and a script 𝓍 can mean three different things, so each one needed its own character to keep that meaning in plain text. The useful part for developers is how the block is laid out. Each style is a neat run of 26 capitals followed by 26 lowercase letters: Style Example Capital A Small a Bold 𝐀𝐚 U+1D400 U+1D41A Italic 𝐴𝑎 U+1D434 U+1D44E Script 𝒜𝒶 U+1D49C U+1D4B6 Bold Script 𝓐𝓪 U+1D4D0 U+1D4EA Fraktur 𝔄𝔞 U+1D504 U+1D51E Double-struck 𝔸𝕒 U+1D538 U+1D552 Sans-serif 𝖠𝖺 U+1D5A0 U+1D5BA Monospace 𝙰𝚊 U+1D670 U+1D68A The block also holds bold, double-struck, sans-serif and monospace digits. Bold 𝟎 starts at U+1D7CE. Because the letters sit in alphabetical order, converting text is just arithmetic. The Character Offset Trick in JavaScript To convert a letter, find its position in the alphabet (A = 0, B = 1 and so on) and add that number to the style's starting code point. That's the whole trick. Here's the simplest version, a bold converter: function toBold(text) {
return [...text].map((ch) => {
const code = ch.codePointAt(0);
if (code >= 65 && code <= 90) return String.fromCodePoint(0x1d400 + (code - 65)); // A-Z
if (code >= 97 && code <= 122) return String.fromCodePoint(0x1d41a + (code - 97)); // a-z
return ch; // leave spaces, numbers and punctuation alone
}).join("");
}

console.log(toBold("Hello, World!")); // 𝐇𝐞𝐥𝐥𝐨, 𝐖𝐨𝐫𝐥𝐝!
Two details matter here: String.fromCodePoint(), not fromCharCode(). These characters are above U+FFFF, so fromCharCode() can't create them in one call. [...text] instead of text.split(""). Spreading a string iterates by code point, so it won't cut a fancy character in half. More on that later. To support every style, store the starting points in a map and reuse one function: const STYLES = {
bold: { upper: 0x1d400, lower: 0x1d41a },
italic: { upper: 0x1d434, lower: 0x1d44e },
script: { upper: 0x1d49c, lower: 0x1d4b6 },
boldScript: { upper: 0x1d4d0, lower: 0x1d4ea },
fraktur: { upper: 0x1d504, lower: 0x1d51e },
doubleStruck: { upper: 0x1d538, lower: 0x1d552 },
sans: { upper: 0x1d5a0, lower: 0x1d5ba },
monospace: { upper: 0x1d670, lower: 0x1d68a },
};

function stylize(text, style) {
const { upper, lower } = STYLES[style];
return [...text].map((ch) => {
const code = ch.codePointAt(0);
if (code >= 65 && code <= 90) return String.fromCodePoint(upper + code - 65);
if (code >= 97 &...
How Fancy Text Generators Work: Unicode Explained
Powered by Readybot.io

The latest from Pixel

Fix GitHub Pages: Missing Artifact Error

Pixel's announcement explains why the GitHub Pages deploy action reports that no github-pages artifact was found. It covers skipped or failed build jobs, missing uploads, …

Rotate DB and Redis Passwords Without Downtime

Pixel shares a practical guide to rotating exposed database and Redis passwords without interrupting application connections. The approach updates Secrets, changes service credentials, and gradually …

AI Agent Receipts Prove What Actually Executed

Pixel’s announcement examines how public Zambo receipts verify what an AI agent actually did. A BTC price fetch succeeded with a recomputable output hash, while …