Loading...
← All projects

AI Humanizer: an offline AI-text detector for UA and EN

AI Humanizer is a Windows app that estimates how much a Ukrainian or English text looks like LLM output and points at the sentences and phrases behind that estimate. Nothing leaves the machine. The heuristics run inside the app, and the deep analysis is done by a model you run yourself in LM Studio.

I've already written about the Joomla component built on top of this app. This post is about the desktop app itself: why it exists, how it differs from the tools people already use, and what's still missing.

When a detector score becomes evidence

AI detectors made their way into academic integrity procedures well before anyone established whether their percentages could carry that weight. OpenAI pulled its own classifier on July 20, 2023. By the company's numbers it caught only 26% of AI-written text and labeled human text as AI 9% of the time.

Independent results aren't more reassuring. Liang and colleagues at Stanford (Patterns, 2023) ran seven popular detectors on writing by non-native English speakers, and the detectors flagged it as AI-generated 61% of the time. On roughly one paper in five, all seven agreed, and all seven were wrong. Ukrainian researchers submitting English manuscripts to Scopus journals fall squarely into that group.

In February 2026 Washington State University cancelled its Turnitin AI detection contract. The provost's memo is blunt. Turnitin analyzed 148,547 assessments at WSU in fall 2024, so even a 1% false positive rate means about 1,485 human-written submissions flagged. From 2023 to 2025, 33% of AI-related integrity cases ended with a "not responsible" finding because the detector report was the only evidence. And students never saw the report, so they couldn't respond before being accused.

Then there's confidentiality. Elsevier tells reviewers and editors not to upload a manuscript, or any part of it, into a generative AI tool, because doing so may breach confidentiality and the authors' proprietary rights. Pasting someone's unpublished manuscript into a cloud detector raises the same problem.

Ukraine's Ministry of Digital Transformation and Ministry of Education and Science published recommendations on responsible AI use in higher education in April 2025. They focus on attribution: students should disclose AI use, and disclosure itself shouldn't lead to accusations. The document says nothing about detectors. So institutions are told to expect honest disclosure, with no guidance on how to check it, and the gap gets filled by cloud services that return a single number.

What exists, and where AI Humanizer differs

Existing tools fall roughly into four groups.

GroupExamplesUkrainianWhere the text goesOutput
Institutional plagiarism suitesTurnitin, StrikePlagiarismStrikePlagiarism claims 100+ languages; Ukrainian isn't named on its listvendor clouddocument score, colored fragments
Standalone cloud detectorsGPTZero, Copyleaks, Originality.ai, ZeroGPTlisted by GPTZero and Copyleaksvendor cloudpercentage, highlighting
"Humanizers"Undetectable.aivariesclouddetection plus automatic rewriting
Research methodsBinoculars, DetectGPTnot a focuslocal, if you have a GPU and patiencea number in a terminal

StrikePlagiarism is upfront that its coefficient "is not a measure of the ratio" of AI text, and it scores individual fragments. None of these tools explains why a fragment turned red, though. Humanizers go the other way and rewrite everything without showing what changed or why. (In October 2025, David Gewirtz found Undetectable.ai's detector mislabeling human text as AI and the reverse.) Binoculars looks excellent on paper, with its authors reporting over 90% detection at a 0.01% false positive rate, but it's a method built on a pair of language models, not something you can hand to a lecturer.

I built AI Humanizer around four requirements that none of the above meets together. The text stays on the computer. Ukrainian gets its own marker lexicons instead of translated English ones. Every score breaks down into reasons a person can check. And getting from "here's a suspicious sentence" to "here's the corrected .docx with its formatting intact" doesn't take three different services.

Three levels of analysis

First, the heuristics. They update as you type and need no model at all. The text is split into sentences and paragraphs, and nine signals are weighted into one score:

SignalWeight
sentence rhythm (coefficient of variation of lengths)2.2
stock LLM vocabulary per 100 words2.4
template transitions at sentence start1.5
"not X, but Y" frames1.3
lists of three1.0
em dashes1.1 for EN, 0.5 for UA
share of mid-length sentences1.2
paragraph uniformity1.0
human markers (first person, brackets, questions)1.6

Dashes weigh less for Ukrainian on purpose, since in Ukrainian the dash is standard punctuation, not a model habit. Each language has its own lexicon, with «відіграє ключову роль» and «у сучасному світі» on one side and delve, tapestry and "it is worth noting" on the other. The final score is stretched toward the ends (0.5 + (score - 0.5) * 1.35) so that a few strong tells outweigh diffuse noise. Confidence depends on length: under 80 words it's low, under 250 it's medium.

Next comes style resemblance to model families (ChatGPT, Claude, Gemini, DeepSeek, Grok). The source says plainly that the marker lists are "hand-picked observations, not a trained classifier," and the UI tags the block as experimental and states that it isn't an authorship claim.

The third level is a deep analysis by a local model through LM Studio. It sits behind a button that only appears once the app finds a running LM Studio server or the lms CLI.

Why LM Studio is called from the main process

An obvious route would be a fetch from the renderer to localhost:1234. That would force every user to turn on CORS in LM Studio's settings, and that's one more step where people give up. So all model requests go through Electron's main process, where Node's fetch has no CORS. The renderer never gets ipcRenderer either; everything crosses the explicit Api surface in shared/types.ts.

More interesting was the JSON schema for the reply. LM Studio supports grammar-constrained decoding, which means the model emits fields in schema order. With ai_probability first, small models committed to "0%" and then spent the rest of the reply arguing the text was AI. I reversed the order:

ts
// Property order matters: grammar-constrained decoding emits fields in this order,
// so the model writes its evidence first and commits to a number last.
export const SCHEMA = {
  type: 'object',
  additionalProperties: false,
  required: ['reasons', 'sentences', 'summary', 'model_guess', 'verdict', 'ai_probability'],
  // …
}

Evidence comes first now, the verdict after it, and the number last. If the number still contradicts the verdict, normalize() pulls it into the matching band and the UI says so. I also needed a lenient parser, because local models return <think> blocks, markdown fences, trailing commas and, most often, an object cut off by the token limit. Since evidence leads the schema, a truncated reply usually still has the reasons and the flagged sentences.

Edits as tracked changes

A detector that just says "72%" does nothing for an author who wrote the text themselves and now has to explain to a committee why it reads as machine-made. Since 0.8.0 the app suggests edits, and since 0.13.0 it does that without LM Studio too.

The offline rules drop template openers ("Moreover,", «Крім того,»), replace clichés with plainer wording, turn "not just X, but Y" into a simple "and," and delete filler like "it is worth noting that." They're phrase swaps, not grammar. In Ukrainian a swap sometimes leaves the next word in the wrong case, and the built-in manual warns about exactly that.

With LM Studio running, the model rewrites the sentences it flagged. The prompt is strict: keep the meaning and every fact, number, name, term and citation, add nothing, never translate, keep academic text academic. The model sees each sentence with its neighbours rather than the whole document, since a long text would overflow LM Studio's default context otherwise.

Both sources land in one "Suggested edits" list. Removed words are struck through in red, new ones marked in green, and every edit can be accepted, rejected or reverted. Nothing reaches the text without a click.

This raises an obvious integrity question. A tool that removes markers can also be used to hide AI use. The app only changes stylistic phrasing and never adds content, but disclosure is outside its scope and should stay there. That's a matter for the author's declaration and the institution's policy.

After editing, Ctrl+S writes the text back into a file of the same format. For .docx, reading and writing share one TextMap, so the flat text in the editor always maps to the same XML runs. A token-level diff touches only changed spans, and every other part of the package is re-zipped byte for byte, so styles, tables, headers and images stay put. A PDF can't be edited that way, so it becomes a new .docx.

The report and the guide

The report exports to PDF with Ctrl+P. There's no PDF library in the project: a hidden Chromium window loads report.html and prints it with printToPDF. The one trap is that a hidden window paints no frames, so the page waits for document.fonts.ready, never requestAnimationFrame. The report's colophon states that the text was processed locally and sent nowhere. For a reviewer, that line may matter more than the score.

A separate window holds the methodological guide, "AI text detection." It covers how detectors work, why they fail, and how authors can protect their writing from false accusations, and it includes a watermark calculator, a base-rate simulator and an author checklist. It works offline.

What's not there yet

  • OCR. A scanned PDF without a text layer produces nothing.
  • Trained lexicons. The style lists are hand-picked observations.
  • Ollama. LM Studio is the only backend for now. Ollama is planned, and adding it should be one row in shared/capabilities.ts plus a client.
  • Streaming. The model reply arrives in one piece.
  • Code signing. The installer is unsigned, so SmartScreen will warn.

Only the first 160 sentences go to the deep analysis, so the text fits the model's context. Builds exist for Windows only (NSIS installer and zip).

Stack and build

Electron 33, React 19, TypeScript 5.7, Vite 6 for the renderer, esbuild for main and preload, SCSS with BEM, pdf.js for reading PDFs, and fflate for re-zipping .docx files. The UI is bilingual (UA/EN), and a separate script checks that both dictionaries carry the same keys. The first commit dates from September 26, 2026; the current version is 0.13.0.

bash
npm install
npm run dev     # Vite + Electron with hot restart
npm run dist    # NSIS installer + zip in ./release

You need Node.js 20+ and Git. There are no native modules, so Visual Studio Build Tools aren't required. npm run dist starts with a smoke test that renders the UI in jsdom, which is what catches a useEffect placed after an early return. That bug has produced a black window before, and neither TypeScript nor Vite said a word.

Sources