OSS · Python CLI
rhythm-lens
Prose rhythm flattens before you notice. Measure it.
rhythm-lens measures the rhythm of Markdown in Japanese, English and Portuguese: sentence-length burstiness, syllable/mora variation, paragraph structure. It locates your document on measured distributions from two published papers and answers in percentiles, not vibes. Language is auto-detected; pass --lang to override.
View on GitHub Japanese paper (DOI) → Cross-lingual paper (DOI) →
The 4 core metrics
All seven measured LLMs drift the same way on these four. Direction is model-universal, magnitude is model-dependent. Effect sizes are taken from the paper.
| Metric | AI direction | Cohen's d |
|---|---|---|
Burstiness (characters) | lower = monotone | −0.96 |
Burstiness (morae) | lower = monotone | −0.80 |
Sentences per paragraph, CV | lower = uniform | −0.67 |
Sentence-length CV (characters) | lower = monotone | −0.56 |
Cross-lingual since v0.2.0
A follow-up paper re-ran the design in English and Portuguese with fresh pre-ChatGPT human corpora (853 Dev.to posts, 403 TabNews posts) against 7 LLMs each. The monotonization direction held in all 70 of 70 model×metric cells. So rhythm-lens ships calibrated baselines for three languages, each with its own frozen distribution.
| Language | Human baseline | Pooled burstiness d | Core metrics d < 0 |
|---|---|---|---|
| Japanese | 696 Qiita/Zenn articles | −0.96 | (per-model, 3rd paper) |
| English | 853 Dev.to posts (2019–2022) | −1.12 | 7/7 models |
| Portuguese | 403 TabNews posts (2022) | −1.03 | 7/7 models |
The direction of rhythm metrics ports across languages; only the thresholds are re-calibrated per language. Punctuation is the exception — comma density flips sign between English (+) and Portuguese (−), so it is a per-language dialect, not a shared accent.
Where the thresholds come from
No hand-tuned magic numbers. Every threshold, distribution and effect size is frozen-generated from the published data of the paper (Imoto 2026, DOI 10.5281/zenodo.21413035).
- Human side: 696 Qiita/Zenn technical articles written 2020-2022, before LLM assistance was common
- AI side: 350 documents generated by 7 LLMs (Claude 3 Haiku / Sonnet 4 / Opus 4 / GPT-3.5 Turbo / GPT-4o / GPT-OSS 20B / Llama 3.2 1B)
- The baseline file is deterministically rebuildable from the paper repository’s public CSVs, and the metric code matches the paper’s definitions, verified against the paper’s numbers
- Output shows where your document sits on the human distribution as a percentile, per metric
Install
pipx install git+https://github.com/kenimo49/rhythm-lens Dependencies are MeCab (mecab-python3 + unidic-lite, for Japanese) and pyphen (syllable approximation for English/Portuguese). No LLM, no API key, no network: fully local and deterministic.
Usage
One file in, one verdict out. Exit is instant on documents of ordinary article length.
rhythm-lens article.md # core metrics + verdict with evidence line
rhythm-lens article.md --all # all 12 metrics
rhythm-lens article.md --json # machine-readable output for CI / editor hooks Relationship to natural-japanese
This tool started from coji/natural-japanese (MIT). Its observation that AI smell lives in rhythm more than vocabulary became the starting point of the paper, and rhythm-lens follows its definitions for burstiness, mora approximation and sentence splitting. github.com/coji/natural-japanese
| natural-japanese | rhythm-lens | |
|---|---|---|
| Form | Agent Skill (an LLM reads and rewrites) | Deterministic CLI (no LLM) |
| Main goal | Fix the prose | Measure it and show the evidence |
| Thresholds | Practitioner observation | Published measured distributions (percentiles) |
Complementary layers rather than competitors: measure with rhythm-lens before and after fixing with natural-japanese.
Limits
- Three languages have baselines (ja/en/pt); other languages, including Spanish, have no calibrated distribution yet
- Calibrated on the technical-blog register; novels, poetry and chat logs follow different distributions
- Documents under 5 sentences are not judged (same minimum as the paper)
- YAML frontmatter is stripped before measuring
- Mora/syllable counts are approximated (UniDic for Japanese, pyphen for en/pt); read them alongside the character-based metrics
Related articles
Related developer tools
- hook-chain-lens A read-only CLI that reads every Claude Code hook scope (user, project, local, plugin) and prints what will actually fire in this cwd, in what order, from where.
- private-lint Git-hook gate that blocks personal and private names (family names, personal emails, client domains) before commit and push. Pure Bash; detection patterns stay machine-local, never in any repo, and a full-history audit covers the moment before you flip a repository public.
- quake-lens Earthquake statistics CLI + MCP server in pure-stdlib Python: Gutenberg-Richter b-value (Aki MLE) and Omori-Utsu aftershock decay (Ogata MLE) from public catalogs.
- claude-shift Multi-account Claude Code manager. Switch the active login, sync the two-file auth Claude Code actually reads (~/.claude/.credentials.json + ~/.claude.json), and watch 5-hour and weekly usage across all accounts. CLI + local API + Chrome extension + browser Web UI + Tauri v2 desktop.