About this Zalgo text generator

A small, dependency-free tool for one job: stacking real Unicode combining marks onto plain text, with controls for how heavy the effect gets. No account, no upload, and nothing you type is ever recorded.

Why it exists

Zalgo / cursed text generators are a genre of tool that has existed for a long time, and most of them are a single fixed intensity with no way to dial it back for a context that needs to stay readable, or forward for maximum chaos. This one exposes the two knobs that actually matter — how many marks, and which of the three mark groups (above, through, below) contribute to them — and adds a straight path back to plain text for anyone who received cursed text and just wants to read it.

How the mark pools were built

Every mark this tool can add comes from the Unicode Combining Diacritical Marks block, U+0300 to U+036F — the same 112-codepoint range used by essentially every Zalgo generator, including the ones that predate this site by well over a decade. Rather than concatenating one big pool, the codepoints are split into three smaller pools by where each one visually renders relative to a base letter:

  • Above — accents, rings, carons, arrows and the small superscript-letter combining marks (U+0363–U+036F), roughly 58 codepoints.
  • Through — the overlay and strike marks (U+0334–U+0338) plus the combining horn, 6 codepoints.
  • Below — hooks, dots, cedillas, under-lines and bridges, roughly 46 codepoints.

That split is a rendering approximation, not a formal Unicode property — a couple of marks are genuinely ambiguous, and font rendering of the exact same codepoint varies. It exists so the three toggles on the tool do something visually distinct, not to make a taxonomic claim.

How the intensity control works

Each intensity level maps to a minimum/maximum count of marks drawn per enabled group, per character: Low draws 0–2, Medium 2–6, Max 6–14. The slider between them interpolates the same way. The count is re-rolled independently for every character and every enabled group, which is what makes the output look ragged rather than like a uniform decorative font — real corruption is uneven, and so is this.

Spaces, tabs and line breaks are never used as attachment points. A combining mark placed after whitespace has no glyph to stack onto, so it would only inflate the character count without changing anything visible — the tool skips it rather than pretend it did something.

How it was verified

The transform is pure — zalgofy(text, options, rng) takes an injectable random-number function instead of calling Math.random() directly — specifically so it can be driven by deterministic stub generators under Node, with no browser and no real randomness involved. The full suite lives at the repo root as zalgo-text.test.js and checks, among other things:

  • Output length never shrinks. Checked against plain, accented, multi-line and emoji input.
  • Every character added is in U+0300–U+036F. Checked two ways: stripping the output's combining marks reproduces the exact input, and separately, every non-mark character left in the output reconstructs the input on its own.
  • Turning off all three mark groups returns the input completely unchanged, across every intensity setting, since “no groups enabled” has to win regardless of how much intensity was requested.
  • Higher intensity adds more marks, holding the random draw fixed so only the intensity setting varies between runs — checked for the three named levels and for a sweep of the 0–100 slider.
  • Base letters survive in their original order. Stripping the combining marks from cursed output reproduces the input exactly, including punctuation, digits, accented letters already present in the input, and mixed-width text.
  • No mark is ever attached after whitespace — the character immediately following a space in the output is always the next real letter, never a stray combining mark.

Every mark pool member's codepoint is also checked individually against the U+0300–U+036F range as part of the same run, so a typo introducing a codepoint from outside the block would fail the suite immediately rather than ship silently.

How the site is built

  • Static HTML, one small stylesheet, two small scripts. No framework, no build step, no bundler. zalgo.js holds the pure transform; app.js wires it to the page.
  • Needs nothing from the network. No CDN framework, no web font, no remote image; the favicon is an inline SVG data URI. Once the page has loaded it works offline.
  • System font stack. Your operating system's UI font, so nothing has to download and nothing shifts as it loads.
  • Nothing you type is transmitted. The whole transform happens in your browser; text you type is never uploaded anywhere.
  • Dark mode follows your system setting via prefers-color-scheme.

Scope and honesty about limits

This tool draws only from the standard Combining Diacritical Marks block. It does not use the supplementary combining-mark blocks Unicode also defines further out in the astral planes, which some other generators pull from for even denser effects — that was a deliberate choice to stay inside the block every mainstream renderer already has reasonably good support for.

The “above / through / below” split is a practical categorisation for the three toggles, not a citation-backed Unicode classification, and is disclosed as such on the tool page. Rendering depth, character-limit behaviour and screen-reader announcement are all decided by whatever surface the text ends up in, not by this tool — see the tool page's “Where it works, and where it breaks” section for specifics.