Notes › Tech Notes › 120 word slots, 15 whole sentences and 1 hard-coded opening: auditing the lorem-ipsum word list and measuring repeated fragments

120 word slots, 15 whole sentences and 1 hard-coded opening: auditing the lorem-ipsum word list and measuring repeated fragments

The generator script was cut out of the template and run in a sandbox: the Latin word list holds 120 entries and 115 unique words, the 5 duplicated words take two slots each at a measured ratio of 1.9727, and the classic opening line is hard-coded, hitting 100.0% of 1000 sampled runs by default.

Regenerate it ten times and the opening three words never move

Every regeneration starts with Lorem ipsum dolor, so does that line join the drawing at all? I cut the generator script out to check. The extracted block is 124 lines and 4682 characters, run in a sandbox unchanged and fed with stub values, while the template itself is 175 lines and 8390 bytes.

The Latin word list: 120 slots holding 115 distinct words

Counting latinWords: the array carries 120 entries and deduplication leaves 115 words, so 5 positions are duplicate slots, with sed at index 8 and 90, id at 59 and 105, est at 60 and 114, voluptas at 85 and 112, omnis at 111 and 115. Words are picked by index, so those 5 get drawn twice as often: over 120000 draws (seed=23) the double-slot words averaged 2011.2 hits against 1019.5 for single-slot words, a ratio of 1.9727. 120 is the entry count; 115 is only how many words stay unique after deduplication.

Mean word length is 5.975 characters: the shortest word do runs 2 characters, the longest, exercitationem, runs 14.

120 slots, and 5 words take 2 slots One-slot 1019.5 hits Two-slot 2011.2, ratio 1.9727

Figure 1: the 5 extra slots hand out double the odds

randInt keeps its upper bound, so a sentence spans 8 to 20 words

The randInt source carries a +1, closing both ends: 100000 calls of randInt(0,2) hit the upper bound 2 exactly 33447 times, and randInt(8, 20) came out on all 13 values, 20 appearing 7613 times. The real call site was run the same 100000 times and reached all 120 indices, the last slot 119 taking 849 hits against an expectation of 833.3, so no off-by-one.

A Latin sentence has no fixed length: over 100000 generated sentences the mean is 13.9854 words and 97.60 characters, and across 400000 sentences the shortest measured 30 characters, the longest 199. Drawing with replacement repeats a word inside one sentence, and 55.57% of the outputs carry such a repeat. Each sentence ends in one ASCII period and carries no comma.

The opening line is pushed in as one whole string at L127

This page is the Lorem Ipsum placeholder text generator. generateLatinParagraph guards on three conditions: only the first sentence of the first paragraph, with the box ticked, gets that classic line pushed in as a whole string. It is not in the word list: 56 characters, 8 words, 1 comma. Running the real generate() 1000 times on the defaults gave Lorem ipsum dolor as the first 3 words in 100.0% of the outputs, while the same protocol unticked gives 0.0%, and distinct openings go from 1 kind to 1000.

The comma is the side evidence: with the box off, 400000 sentences hold 0 commas; with it on, 100000 sentences hold exactly 500, one per run. At the default 3 paragraphs by 5 sentences this zero-random piece is only 3.89% of the characters and 6.67% of the sentences. The reproducibility note: the page offers no seed, the token seed appears 0 times in the source, and every distribution above came out only after the probe replaced Math.random.

1000 runs at one setting, and only a single sentence repeats

The protocol: 3 paragraphs by 5 sentences run 1000 times, that is 15000 sentences, 3000 paragraphs, 1000 whole outputs. Latin with the box on gives 14001 distinct values among 15000 sentences and exactly 1 repeated key, which is the classic line in 1000 copies; the longest repeated sentence runs 8 words and 56 characters, and paragraphs and whole outputs repeat 0 times. Latin with the box off keeps all 15000 sentences distinct. The Chinese branch's 15000 sentences come from only 15 values, each appearing between 967 and 1055 times, and its 3000 paragraphs collide into 5 repeated paragraphs; the space is 15 to the fifth power, that is 759375, whose birthday expectation is 5.93. Squeezing sentences per paragraph to 1 leaves only 868 distinct outputs, the commonest repeated 3 times. Without replacing Math.random the same structure holds.

3 by 5 settings, 1000 runs: 15000 sentences / 3000 paragraphs / 1000 files Latin on 1 repeated key: the classic line, 1000 copies Latin off All 15000 sentences distinct, 0 repeats

Figure 2: what repeats across 1000 runs

The Chinese branch is 15 whole sentences, not a character pool

chineseSentences holds 15 entries, unique across the whole table, each 20 to 25 characters and 22.2 on average, 333 characters in all, which is 999 bytes. The full stop is written into every string: the branch copies whichever sentence it drew, with no randomness between characters. generateChineseParagraph joins with an empty string, adding no spaces or line breaks inside a paragraph; one measured paragraph of 5 sentences came out at 112 characters, and reverse lookup returned whole library entries only.

Those entries are measured output of the page under test, so they keep their simplified glyphs here instead of being translated; this article is not written in Chinese. One verbatim: 前端工程化已成为大型项目开发的标配实践。

Characters and bytes are two separate ledgers

SettingLatin branchChinese branch
3 paragraphs, 5 sentences each1433 characters = 1433 bytes335 characters = 997 bytes
20 paragraphs, 10 sentences each19776 characters = 19776 bytes4458 characters = 13298 bytes
Third cell of the stat barword count 201character count 335

The Latin branch is pure ASCII, non-ASCII bytes 0. In the Chinese branch every full-width character costs exactly 3 bytes, and the 335 characters of one default run include 4 line breaks, so 331 times 3 plus 4 gives 997. At the same settings the character ratio is 0.2338 and the byte ratio 0.6957. The third cell of the Chinese branch reads 字数 and always equals the 总字符数 cell, punctuation and line breaks counted; it is not a hanzi count and must not be quoted as a byte figure.

Bad input falls back in silence, and the page owns no error copy

The chain is parseInt, a falsy value falling back to the default, then clamping, so min and max attributes constrain nothing in the script. Typing 0 into the paragraph box gives the default 3 paragraphs, not the lower bound of 1; only negative input clamps to 1. The value 10000 clamps to 20 paragraphs by 10 sentences and measured 19466 characters in 2.439 ms, while the default setting run 5000 times averaged 0.165 ms. parseInt also reads 0x10 as 16 paragraphs and stops at the 1 inside 1e3. Literal counts of try, catch, alert and of 错误 are all 0, download and Blob are 0 too, and the single exit this page offers is the copy button.

The checkbox rules only the Latin branch: generateChineseParagraph takes a single argument and never reads it, so under the same seed the Chinese branch gives byte-for-byte identical output ticked or not, both 345 characters. shuffle appears once in the 175 lines of the template, on its definition line L102, with 0 call sites: dead code touching no branch. Sentences are not scrambled word lists either; among 840510 adjacent word pairs the classic original's adjacent pairs came up only 618 times, 0.0735%, the same order as the 58.37 expectation of a draw that ignores the previous word, and sed do reached 120 only because sed occupies 2 slots. How a shuffle should be written is another note, which shuffle implementation to pick.

The 11 checks between the copy and the code

Judged one by one, the 11 verdicts split into 6 match, 3 in part, 2 conflict. The first conflict is the claim that the output is not random text, since on the default setting only the opening 56 characters are determined. The second is the placeholder hint in the output box: the script runs generate() once at the end unconditionally, so the first screen already carries a value and that hint never shows. Two edges stay unmeasured here: whether UU.copy really reaches the clipboard, and what a browser's native number input does with min and max; the first screen came from a Node sandbox with a stubbed DOM, not a live browser.

FAQ

Q: Why does the opening read Lorem ipsum dolor sit amet every time?

It is one whole string in the source, pushed in only for the first sentence of the first paragraph with the box ticked; over 1000 sampled runs it hit 100.0%, the same protocol unticked gives 0.0%.

Q: Is the word list 120 entries or 115 words?

The array is 120 entries, and 115 is only the count of distinct words left after deduplication. Because words are picked by index, the 5 that occupy 2 slots each are drawn at double the odds, measured at a ratio of 1.9727. How much copy a container needs is another question, in the older note placeholder text that does not look like a template.

Q: Does the Chinese branch assemble characters at random?

No. It is a library of 15 whole sentences: each drawn entry is pasted unchanged, paragraphs join with an empty string and carry no separators, and once sentences per paragraph drop to 1 the 1000 whole outputs collapse into 868 distinct results.

📎 Related articles

SVG preview sizes: the box equals the container, and r=1 measures 2 pixels once viewBox is removed 2026-11-05 Empty Fields Survive the Text Column Split: a,,b Stays, an Index Out of Range Fills Blank and 0 or abc Returns Every Column 2026-11-05 13 Digits In, 11 Out: the Regex Boundaries of the Seven Built-in Types in text-extract 2026-11-05 How a 1 GB File Is Built From 1024 Chunks: Chunk Rhythm, Cancel Boundaries and Three Fill Bytes in a File Generator 2026-11-04 Three Edges of Filename Extraction: The Trailing Slash Backstops One Step, the Query String Stays Whole, Blank Lines Vanish Silently 2026-11-04