How a Lorem Ipsum Generator Works: The Word Bank (2026)
Every lorem ipsum generator is a small word bank plus a few rules. Here is the actual algorithm: 182 Latin words, sentence windows, commas, punctuation.
How a Lorem Ipsum Generator Works: The Word Bank
Click "generate" on a lorem ipsum generator and you get Latin-looking prose that reads like real text but says nothing. There is no corpus behind it and no language model. It is a fixed list of words, a random number generator, and about forty lines of code. Once you have seen the algorithm, you can predict exactly why the output looks the way it does — and where it falls short.
TL;DR
- A generator is a word bank, a sentence-length range, and comma rules.
- Emmet's Latin dictionary is 182 words plus an 8-word opening phrase.
- Words come from Cicero's de Finibus 1.10.32–1.10.33, deduplicated.
- Sentences sample without replacement, so no word repeats inside one.
- Only seeded generators like Faker produce reproducible filler text.
How does a lorem ipsum generator work?
Strip away the UI and every generator does the same four things in the same order.
The three parts every generator has
First, a word bank: a flat array of tokens. Second, a common phrase: the lorem ipsum dolor sit amet… opener, stored separately so it can be turned on or off. Third, a set of shape rules — how many words per sentence, how many commas, which punctuation mark ends it.
That is the whole design. Lipsum.com describes its own engine the same way, as a dictionary of over 200 Latin words combined with a handful of model sentence structures.
The generation loop, step by step
The loop in Emmet's lorem module is short enough to read in one sitting:
paragraph(dict, wordCount):
if startWithCommon:
emit sentence(common[0..wordCount], ".")
while total < wordCount:
n = min(rand(2, 30), wordCount - total)
emit sentence(sample(dict.words, n))
rand(from, to) is exclusive at the top, so a sentence gets between 2 and 29 words. The final sentence is clipped so the paragraph lands on the exact requested count rather than overshooting.
Why generators sample without replacement
Emmet's sample() builds a Set, not an array. Words are drawn until the set reaches the target size, which means a single sentence never contains the same word twice. That is not realistic — real prose repeats function words constantly — but it makes short filler look less broken. Faker takes the opposite approach and allows repeats, which is why a long faker.lorem.paragraphs() block visibly recycles et, ut, and qui.
Where do the Latin words in lorem ipsum come from?
The word bank is not arbitrary. It is a vocabulary list extracted from one passage of classical Latin.
Cicero's de Finibus, sections 1.10.32 and 1.10.33
Per lipsum.com's own reference page, the source is de Finibus Bonorum et Malorum, written by Cicero in 45 BC, specifically sections 1.10.32 and 1.10.33. Read those two sections and the generator's dictionary stops looking random: accusamus, iusto, dignissimos, ducimus, blanditiis, praesentium, voluptatum, deleniti, atque and corrupti all appear there, in that order, in a single sentence of 1.10.33.
We covered the full paper trail — Cicero, the 1914 Rackham translation, Letraset in 1966 — in a separate article linked at the bottom. What matters here is the mechanical consequence: the word bank is the passage's vocabulary with duplicates removed.
How 182 words became the standard bank
Emmet's latin.json ships exactly 182 entries in its words array, plus 8 in common. Deduplicating two paragraphs of Cicero gets you to roughly that number, and every generator that copied its dictionary from an earlier generator inherited the same ceiling. That is why filler from different tools feels interchangeable: they are drawing from near-identical lists.
Why the word list is not really Latin
The bank contains real Latin lemmas, but the assembled sentences have no grammar. Cases do not agree, verbs do not conjugate to their subjects, and word order carries no meaning. That is deliberate — the point is a plausible distribution of word lengths and letter frequencies, not a translatable sentence. It is also why you should mark filler with lang="la" in HTML so screen readers do not attempt to pronounce it as English.
Why does lorem ipsum always start with "Lorem ipsum dolor sit amet"?
Because the opener is hard-coded, and it is the only part of the output that is not random.
The common array and the skipCommon flag
Emmet stores the opener as its own array and prepends it to the first sentence unless you opt out:
{
"common": ["lorem", "ipsum", "dolor", "sit",
"amet", "consectetur", "adipisicing",
"elit"],
"words": ["exercitationem", "perferendis", "…"]
}
Note the spelling: adipisicing, with an extra i. The 1966 Letraset passage reads adipiscing, and Cicero's original reads adipisci velit. Somewhere in the copy chain a typo entered a dictionary file and then propagated to every editor that bundles Emmet. If your placeholder text says adipisicing, you now know which generator produced it.
When the opener gets dropped
The module exposes a skipCommon option, and it also suppresses the opener when a lorem node is generated in a repeating context. The practical effect is that the familiar phrase belongs to the first sentence of a block, not to every paragraph. Turn it off entirely and the output is still valid filler — it just stops announcing itself.
lorem vs lorem100 vs p*4>lorem
Per the Emmet documentation, the bare lorem abbreviation expands to a 30-word block, and a numeric suffix overrides the count:
lorem → 30 words
lorem100 → 100 words
p*4>lorem → four filled paragraphs
ul>lorem6*5 → five list items, 6 words each
The last form relies on Emmet's implicit tag resolver, which is why you can drop the li.
What makes generated filler look like real prose
Three shape rules do all the work. Get them wrong and the output reads as noise.
Sentence length: the rand(2, 30) window
A uniform draw from 2 to 29 words produces an average sentence around 15 words — close enough to English editorial prose that a paragraph block looks typeset rather than random. Faker uses a tighter default, { min: 3, max: 10 } for sentence(), and composes a paragraph() from 3 sentences by default. Tighter windows read as punchier copy; wider windows produce the ragged look designers expect from body text.
Comma density scales with sentence length
Emmet buckets sentences and assigns a comma budget:
| Sentence length | Commas inserted |
|---|---|
| 2–3 words | 0 |
| 4–6 words | 0 |
| 7–12 words | 0 or 1 |
| 13+ words | 1 to 3 |
Commas are placed at random positions, never on the final word, and never doubled on a word that already has one. It is crude, and it works: comma frequency, not comma placement, is what the eye reads as "this is a sentence".
Punctuation weighting, and an off-by-one worth knowing
The terminal mark is chosen from the string ?!... — one question mark, one exclamation mark, three periods — which was clearly meant to make periods three times more likely. But the helper indexes with rand(0, val.length - 1), and since rand is already exclusive at the top, the last character is unreachable. The effective distribution is one period in two, not three in five:
| Intended | Actual |
|---|---|
. 60% |
. 50% |
? 20% |
? 25% |
! 20% |
! 25% |
Harmless in filler text. A useful reminder that "pick a random element" is one of the easiest functions in the world to get subtly wrong — the same class of bug that shows up in UUID and token generation, where it is not harmless at all.
How to generate the same lorem ipsum every time
Most in-browser generators call Math.random(), which JavaScript does not let you seed. Every run gives you new text. That is fine for a mockup and useless for a test suite.
Seeded generators with Faker
Faker solves this with a pluggable randomizer. Its documented default since v9 is a 53-bit Mersenne Twister, and seeding it makes the whole pipeline deterministic:
import { faker } from '@faker-js/faker';
faker.seed(42);
const a = faker.lorem.paragraphs(2);
faker.seed(42);
const b = faker.lorem.paragraphs(2);
// a === b, on every machine
The Faker lorem API exposes the full ladder — word(), words(), sentence(), sentences(), lines(), paragraph(), paragraphs(), plus slug() for URL-safe hyphenated strings.
Math.random() versus a Mersenne Twister
| Property | Math.random() |
Faker's randomizer |
|---|---|---|
| Seedable | No | Yes |
| Reproducible across runs | No | Yes |
| Good enough for filler | Yes | Yes |
| Safe for secrets | No | No |
Neither is cryptographically secure. If you need unpredictable output rather than merely varied output, that is a different tool entirely — see the password generator, which draws from crypto.getRandomValues().
When determinism actually matters
Three cases justify the extra dependency: snapshot tests, where regenerated filler creates false diffs on every run; visual regression suites, where a longer paragraph shifts layout and fails the pixel comparison; and shared fixtures, where two developers debugging the same seeded dataset need identical text. Everywhere else — mockups, CMS drafts, spacing checks — unseeded output is fine and one less package to install.
Choosing a generator by what it actually does
| Generator | Word bank | Seedable |
|---|---|---|
Emmet lorem |
182 + 8 opener | No |
Faker lorem |
Several hundred | Yes |
| Lipsum.com | 200+ | No |
| iKit Lorem Ipsum | Classic passage | No |
The practical difference is repetition. A 182-word bank starts visibly recycling vocabulary somewhere past 600 words, so if you are filling a long-form article template, generate several smaller blocks rather than one enormous one. Paste the result into a word counter to hit an exact target, or into a Markdown editor if you want the filler already wrapped in headings and lists.
References
- “Lorem Ipsum” generator — Emmet Documentation — abbreviation syntax, the 30-word default, and repeated-element behaviour.
- emmetio/lorem — Transforms parsed Emmet abbreviation node into Lorem Ipsum stub text — source for the generation loop, sampling, comma buckets and punctuation weighting;
lang/latin.jsonfor the 182-word bank. - Lorem Ipsum - All the facts - Lipsum generator — dictionary size, model sentence structures, and the Cicero de Finibus 1.10.32–1.10.33 attribution.
- Lorem | Faker — method signatures and default word and sentence counts.
- Randomizer | Faker — the Mersenne 53-bit default and seeding behaviour.
Related on iKit
- Read the full history behind the word bank, from Cicero to Letraset — where the 182 words came from, and why the 1500s printer story is a myth.
- Start with the practical guide to using filler text in 2026 — how much filler a mockup needs and where to generate it.
- Generate markup-ready filler instead of plain paragraphs — the Emmet abbreviations above, applied to real HTML scaffolding.
- Fill a README or docs page without breaking the renderer — which characters in random Latin quietly become Markdown syntax.
- Swap the Latin for themed filler when Latin is the wrong signal — Bacon, Cat and Hipster Ipsum use the same algorithm with a different word bank.
- Know when filler is hiding a design problem — the layouts that only break once real content arrives.
Related posts
XMLHttpRequest to xml_http_request: The Acronym Rule (2026)
XMLHttpRequest becomes xml_http_request only if your converter knows where the acronym ends. Here is the boundary rule, the regex, and what style guides say.
kebab-case in CSS, URLs, and CLI Flags: Why Hyphens Win (2026)
kebab-case shows up in CSS properties, URL slugs, and command-line flags for reasons that are not stylistic. Here is what the specs actually require.
Where Does Lorem Ipsum Come From? The Real Story (2026)
Where does Lorem Ipsum come from? Not an unknown printer in the 1500s. The paper trail runs from Cicero in 45 BC to a 1914 Loeb edition to Letraset in 1966.