AI Humanizer
A privacy-first, fully client-side tool that strips the typographic tells of AI writing, restores plain ASCII, and runs with zero network calls. Built as a static Next.js export with accessibility baked in.
AI Humanizer is a small side project that solves one narrow problem well. Text pasted from a language model is full of typographic tells (long dashes, curly quotes, fancy ellipses, non-breaking spaces, invisible zero-width characters) that quietly mark it as machine-generated. AI Humanizer catches those, restores plain ASCII punctuation, and lets you redact or replace specific words. Everything runs in the browser. The text never leaves the device.
The problem
Paste a paragraph from a chatbot into a CMS, an email, or a code comment and you carry a fingerprint with you: long dashes where a plain hyphen belongs, curly quotes instead of straight ones, an ellipsis glyph instead of three periods, and characters you cannot even see. Most “humanizer” tools online fix this by sending your text to a server, which is exactly what you do not want for anything sensitive.
The goal was a tool that:
- Cleans the punctuation that language models lean on
- Never makes a network call: no servers, no cookies, no analytics
- Works as a fast static page
- Is fully usable with a keyboard and a screen reader
What it does
- Symbol cleaning: replaces long dashes, curly quotes, ellipses, non-breaking spaces, bullets, arrows, math symbols, and invisible characters with plain ASCII equivalents. Each rule is individually toggleable.
- Word redaction: find/replace rules with low/mid/high matching strictness and live match counts as you type.
- Privacy by architecture: text and settings live only in the browser’s localStorage. There is no backend to leak to.
- Quality-of-life: light/dark theme, configurable editor size, line numbers, a sticky nav, and an import/export of your settings.
Stack
| Layer | Choice |
|---|---|
| Framework | Next.js 16 (App Router, static export) |
| UI | React 19 + TypeScript |
| Styling | Tailwind CSS 4 |
| Tests | Vitest (run in CI on every push) |
| CI/CD | GitHub Actions (lint, typecheck, and tests on every push and PR) |
| Hosting | Cloudflare Pages (static out/) |
There is no server. output: “export” in next.config.ts produces a fully static site, so the entire app is HTML, CSS, and JS on a CDN, with nothing to run or maintain in production.
Client-side by design
Every transformation is pure text processing in lib/textProcessing.ts, covered by unit tests. Because there is no API, privacy is built into the architecture. There is no server that could store or leak your text. The page makes zero fetch calls at runtime, so the only network activity is loading the static assets themselves.
State persistence is just localStorage, wrapped in a small typed storage module so reads are validated rather than trusted. Clearing the tab clears everything.
Accessibility
Accessibility was part of the build from the start:
- Native semantics first. The settings and help dialogs are built on the native <dialog> element, which gives focus trapping, Escape-to-close, and a backdrop for free instead of re-implementing them imperfectly in JavaScript. onCancel is wired to the same close handler so keyboard and click stay in sync.
- Labels everywhere. Icon-only controls carry aria-labels (for example Copy text, Close Settings), and dialogs are tied to their headings with aria-labelledby.
- Dynamic feedback is announced. The copy button swaps its label to Text copied so screen-reader users get the same confirmation sighted users do.
- Keyboard parity. Focus moves to the editor on load, every action is reachable by keyboard, and focus order follows the visual layout.
- Respects user preferences. Animations are gated behind motion-safe / prefers-reduced-motion, and the light/dark theme follows the system. Color choices aim for high text contrast.
Performance
A static export is already fast, but two things pushed it further:
- Subset icon font. The Material Symbols font is subset to only the handful of glyphs the app actually uses: roughly 3 KB instead of 3.8 MB. A Python script scans the codebase for icon names and regenerates the subset, and the result is committed so normal builds need no extra tooling.
- Immutable caching. public/_headers sets long-lived immutable caching on the fingerprinted /_next/static/* assets, so repeat visits are essentially instant.
SEO constants (canonical URL, Open Graph, sitemap, robots.txt) come from a single siteConfig source of truth, with a build-time guard that fails the production build if the real site URL is not set, so a placeholder domain can never ship by accident.
Deployment
Connected to Cloudflare Pages: build with npm run build, publish the out/ directory, set NEXT_PUBLIC_SITE_URL, and every push deploys automatically. Free static hosting with branch previews.
Result
The finished tool cleans AI typography entirely in the browser. There are no accounts and no tracking, and because it ships as a static export there is no server to run or pay for. It loads fast and stays usable with a keyboard and a screen reader.
Thanks for reading!
I hope you found this article helpful, interesting, or useful! If you have questions, or just want to chat about web development, I'd love to hear from you.