DEV Community

Devanshu Biswas
Devanshu Biswas

Posted on

How a Lorem Ipsum generator actually builds fake Latin from a real word pool

Every design tool, CMS theme, and Figma mockup leans on the same trick: when the real words aren't ready, you fill the layout with meaningless text so you can judge shape instead of copy. That filler is lorem ipsum, and I always assumed it was a static blob you paste from a website. Then I built a generator that makes it fresh in the browser, and it turned out to be a genuinely nice little engineering problem.

You can play with the live version here: https://dev48v.infy.uk/solve/day27-lorem.html

Why fake text at all

When you're designing a page, the copy is still being written, the CMS is empty, the API isn't wired up. But you still need to see how the layout breathes: line length, paragraph rhythm, where headings land, whether a card overflows on the third line. Placeholder text fills that gap, and it's deliberately unreadable so nobody starts editing the message instead of the design. Typesetters have used "greeking" like this for centuries, long before the web existed.

The passage is scrambled Cicero

Here's the part I didn't know: "Lorem ipsum dolor sit amet..." isn't random gibberish. It's mangled Latin from Cicero's De Finibus Bonorum et Malorum, written in 45 BC. A printer in the 1500s is thought to have jumbled a passage to make a type specimen sheet, and it stuck. The visible words come from dolorem ipsum quia dolor sit amet — "there is no one who loves pain itself."

That origin matters for a practical reason. Because the text is Latin-like, it carries the letter frequencies and word lengths of real Western European prose, so it looks convincingly like writing without being readable. English filler like "content content content" would distort the visual texture and defeat the point.

A pool of words, not one paragraph

A generator doesn't need the exact passage. It needs a bank of lorem-flavoured words to draw from — around 120 of them, mixing short ones (ut, sed, ea) with long ones (consectetur, perspiciatis, accusantium). That mix is what makes a block look like prose rather than a monotonous list. From there, the whole thing is a two-level hierarchy: words become sentences, sentences become paragraphs.

function sentence(rng, min, max) {
  const n = randInt(rng, min, max);
  const w = [];
  for (let i = 0; i < n; i++) w.push(pick(rng, WORDS));
  w[0] = w[0][0].toUpperCase() + w[0].slice(1);
  return w.join(" ") + ".";
}
Enter fullscreen mode Exit fullscreen mode

Three small touches — a random length, an initial capital, a terminal period — turn a bag of words into something that scans as a sentence. Add a comma or two at interior positions in the longer ones and it gets a real rhythm, echoing the way the classic passage breaks after "amet."

The bug that taught me to seed the randomness

My first version used Math.random(), and it was maddening. Every time I changed the paragraph count or flipped to HTML output, the entire text reshuffled. You'd copy a block, tweak one option, and the thing you were looking at was gone.

The fix is a seeded pseudo-random generator. Given the same seed, it returns the exact same "random" sequence every time, so the text stays stable while you adjust formatting and only changes when you deliberately ask for new text. It also makes output reproducible: same seed, same lorem, on any machine. Math.random() can't be seeded, but a tiny PRNG like mulberry32 can:

function mulberry32(a) {
  return function () {
    a |= 0; a = (a + 0x6D2B79F5) | 0;
    let t = Math.imul(a ^ a >>> 15, 1 | a);
    t = (t + Math.imul(t ^ t >>> 7, 61 | t)) ^ t;
    return ((t ^ t >>> 14) >>> 0) / 4294967296;
  };
}
Enter fullscreen mode Exit fullscreen mode

That single change is what made the tool actually pleasant to use.

Generate first, format last

The last idea that kept the code clean was separating generating the plain blocks from formatting them. You produce the text once, then apply a wrapper as a separate pass: plain text, a <p> per paragraph, a <ul><li> list, or prose with a few random <b>/<i> words for testing how bold and italic runs affect line wrapping.

blocks.map(b => `<p>${b}</p>`).join("\n");
Enter fullscreen mode Exit fullscreen mode

Because words are joined by single spaces and tags carry no spaces, this keeps two invariants honest: three paragraphs always become exactly three <p> tags, and the reported word count is identical no matter which wrapper you pick. I actually wrote a small test that requests N paragraphs, counts the <p> tags, and strips the tags to confirm the word count survives — it does.

The rule nobody codes

Lorem is scaffolding, never a finish. Designing entirely with it hides the real problems: actual copy has awkward long names, empty states, and edge cases the neat filler never reveals, so a layout that looks perfect in lorem can break the day real content lands. Ship realistic content as early as you can. And never leave lorem in production — screen readers will read "Lorem ipsum dolor sit amet" aloud to a blind user, search engines index the gibberish, and it embarrassingly leaks onto live pages.

Placeholder text belongs in the mockup, not the deploy. The interactive build, with live word and character counts, is here: https://dev48v.infy.uk/solve/day27-lorem.html

Top comments (0)