How Unicode Combining Marks Work
The same zero-width characters that let you type café are what make Zalgo text possible. Here is how they work, why the standard was designed this way, and the trap they set for anyone trying to clean text.
What a combining mark actually is
Most characters you type occupy space. A combining mark does not. It is a character with zero advance width whose job is to decorate the character immediately before it.
Type e then U+0301 and you get é — one visible letter built from two characters. Type
a then U+0308 and you get ä. The renderer positions the mark over the base character and moves
on without advancing the cursor.
Unicode calls the pair a grapheme cluster: a sequence of codepoints that a human reads as one character. This distinction matters constantly in programming — the string é may have a length of 1 or 2 depending on how it was typed.
Why the standard works this way
The alternative would have been to encode every letter-plus-accent combination as its own character. That approach fails quickly. Vietnamese routinely stacks a tone mark on an already-modified vowel. Several African and Indic scripts combine multiple marks on one base. Academic transcription systems invent combinations as needed.
Encoding every possibility would require an unbounded number of codepoints and still miss whatever someone invented next week. Combining marks solve it generatively: encode the base letters once, encode the marks once, and let any combination be expressed.
Unicode does include a few thousand precomposed characters — é, ñ, ü and friends — but purely for backwards-compatibility with older encodings, not because they were the plan.
The blocks the marks live in
| Block | Range | What is in it |
|---|---|---|
| Combining Diacritical Marks | U+0300–U+036F | The core set: acutes, graves, tildes, rings, hooks — the marks Zalgo mostly uses |
| Combining Diacritical Marks Extended | U+1AB0–U+1AFF | Additions for phonetic and medievalist notation |
| Combining Diacritical Marks Supplement | U+1DC0–U+1DFF | More specialist marks, many for German dialectology |
| Combining Diacritical Marks for Symbols | U+20D0–U+20FF | Arrows, rings and overlays used in mathematics |
| Combining Half Marks | U+FE20–U+FE2F | Marks that span two base characters |
Zalgo generators draw almost entirely from the first block, split into three groups: marks that sit above the letter, marks that overlay it, and marks that sit below. That is exactly what the three checkboxes on our generators control.
Normalisation, and why cleaning text is tricky
Because é can be stored two different ways, Unicode defines normalisation forms to convert between them:
- NFC (composed) — combine base+mark into a precomposed character wherever one exists. What most systems store.
- NFD (decomposed) — split precomposed characters back into base+mark.
This creates a real trap for anyone writing a Zalgo cleaner. The obvious approach — normalise to NFD, strip everything in the combining ranges — also destroys legitimate accents, turning café into cafe.
Normalise to NFC first instead and you get the opposite bug: base+mark pairs recompose into precomposed letters, so some Zalgo marks silently survive as accented characters.
Other things combining marks are used for
- Strikethrough and underline. U+0336 draws a line through the previous character; U+0332 draws one under it. That is the entire mechanism behind our strikethrough and underline tools — and why the underlying text stays searchable.
- Enclosing marks such as U+20DD draw a circle around the previous character.
- Phonetic transcription relies on them heavily; IPA would be impossible without.
- Historical and liturgical texts use marks for cantillation, vowel pointing and scribal abbreviation.
See it in action
Watch marks stack in real time as you drag the craziness slider.
Open the Zalgo generatorPractical notes for developers
- Never assume one codepoint equals one character. Iterate over grapheme clusters, not code units, or you will split a letter from its mark.
- Normalise before comparing. Two strings that look identical can differ byte for byte. NFC is the usual choice for storage and comparison.
- Consider limiting marks in user input. Capping consecutive combining characters — say, four per base — blocks Zalgo spam without breaking any real language.
- Clamp overflow in CSS. Marks can paint outside their line box;
overflow: hiddenon the container stops one comment covering others. - Do not strip marks blindly. You will break every language that needs them. Limit, do not remove.
More guides
What Is Zalgo Text?
The full story: the creepypasta character, the Unicode loophole that made it possible, and why the effect outlived the meme.
✨Every Unicode Font Style Explained
A tour of the Unicode blocks behind bold, italic, script, gothic, bubble and full-width text, with the gaps in each set.
Frequently asked questions
What is a Unicode combining mark?
A zero-width character that decorates the character before it — an accent, a stroke, a ring. It takes no space of its own and forms a single visible unit with its base.
Why does Unicode allow unlimited combining marks?
Because several writing systems legitimately need multiple marks on one letter, and capping the number would break them. Zalgo exploits that open-ended design.
What is the difference between NFC and NFD?
NFC composes base+mark pairs into precomposed characters where possible; NFD splits them apart. Choosing the wrong one is the usual cause of broken text cleaning.
How do I strip Zalgo without losing accents?
Remove combining characters from the raw string without normalising first. Precomposed accented letters are single characters and survive.
Do combining marks affect string length?
Yes. A letter with three marks is four codepoints. Always work with grapheme clusters when counting or slicing user-visible characters.
How can a site block Zalgo spam?
Limit consecutive combining marks per base character — around four is generous for real languages — rather than stripping them, which would break many scripts.